loading
Generated 2021-03-09T22:15:18+09:00

All Files ( 50.98% covered at 4.72 hits/line )

279 files in total.
13818 relevant lines, 7045 lines covered and 6773 lines missed. ( 50.98% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/channels/application_cable/channel.rb 0.00 % 4 4 0 4 0.00
app/channels/application_cable/connection.rb 0.00 % 4 4 0 4 0.00
app/controllers/api/v1/books_controller.rb 0.00 % 26 23 0 23 0.00
app/controllers/api/v1/categories_controller.rb 0.00 % 47 40 0 40 0.00
app/controllers/api/v1/formats_controller.rb 0.00 % 12 11 0 11 0.00
app/controllers/api/v1/problems_controller.rb 45.45 % 56 33 15 18 0.45
app/controllers/application_controller.rb 100.00 % 2 1 1 0 1.00
app/jobs/application_job.rb 0.00 % 7 2 0 2 0.00
app/mailers/application_mailer.rb 0.00 % 4 4 0 4 0.00
app/models/application_record.rb 0.00 % 3 3 0 3 0.00
app/models/author.rb 100.00 % 6 3 3 0 1.00
app/models/authors_book.rb 100.00 % 2 1 1 0 1.00
app/models/book.rb 100.00 % 11 6 6 0 1.00
app/models/books_problem.rb 100.00 % 2 1 1 0 1.00
app/models/category.rb 83.78 % 56 37 31 6 1.59
app/models/category_relationship.rb 0.00 % 4 4 0 4 0.00
app/models/format.rb 100.00 % 12 4 4 0 1.00
app/models/period.rb 100.00 % 21 10 10 0 1.50
app/models/problem.rb 0.00 % 31 27 0 27 0.00
lib/capistrano/tasks/unicorn.rb 0.00 % 65 48 0 48 0.00
vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/channel/test_case.rb 52.75 % 310 91 48 43 0.53
vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/connection.rb 100.00 % 22 16 16 0 1.00
vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/connection/test_case.rb 50.63 % 234 79 40 39 0.51
vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/test_helper.rb 28.21 % 133 39 11 28 0.28
vender/bundle/ruby/2.6.0/gems/actionmailbox-6.1.3/lib/action_mailbox/test_helper.rb 52.94 % 96 17 9 8 0.53
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/base.rb 40.83 % 1031 218 89 129 0.41
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/collector.rb 52.38 % 32 21 11 10 0.52
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/delivery_job.rb 61.11 % 45 18 11 7 0.61
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/delivery_methods.rb 67.65 % 82 34 23 11 0.94
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/inline_preview_interceptor.rb 54.17 % 57 24 13 11 0.54
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/log_subscriber.rb 38.89 % 39 18 7 11 0.39
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/mail_delivery_job.rb 47.37 % 43 19 9 10 0.47
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/mail_helper.rb 30.43 % 72 23 7 16 0.30
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/message_delivery.rb 37.78 % 178 45 17 28 0.38
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/parameterized.rb 52.27 % 172 44 23 21 0.52
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/preview.rb 55.93 % 142 59 33 26 0.56
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/rescuable.rb 64.29 % 29 14 9 5 0.64
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/test_case.rb 59.42 % 121 69 41 28 0.86
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/test_helper.rb 42.31 % 162 26 11 15 0.42
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/asset_paths.rb 100.00 % 12 5 5 0 1.20
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/base.rb 77.78 % 299 90 70 20 12.18
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/caching.rb 80.56 % 66 36 29 7 1.08
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/caching/fragments.rb 37.50 % 150 48 18 30 0.48
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/callbacks.rb 86.11 % 224 36 31 5 2.42
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/collector.rb 61.11 % 42 18 11 7 6.11
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/error.rb 100.00 % 6 2 2 0 1.00
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/logger.rb 100.00 % 14 7 7 0 1.57
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/rendering.rb 78.57 % 127 56 44 12 0.79
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/translation.rb 47.06 % 37 17 8 9 0.47
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/url_for.rb 84.62 % 35 13 11 2 0.85
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/api.rb 80.00 % 150 15 12 3 1.73
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/api/api_rendering.rb 75.00 % 16 8 6 2 0.75
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/base.rb 80.00 % 273 20 16 4 2.45
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/caching.rb 80.00 % 44 10 8 2 0.80
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/form_builder.rb 80.00 % 50 10 8 2 0.80
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/log_subscriber.rb 80.49 % 81 41 33 8 1.12
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal.rb 64.15 % 258 106 68 38 0.74
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/basic_implicit_render.rb 83.33 % 13 6 5 1 1.00
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/conditional_get.rb 48.65 % 288 37 18 19 0.54
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/content_security_policy.rb 55.56 % 51 27 15 12 0.56
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/cookies.rb 87.50 % 18 8 7 1 0.88
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/data_streaming.rb 32.43 % 151 37 12 25 0.32
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/default_headers.rb 100.00 % 17 7 7 0 1.00
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/etag_with_flash.rb 100.00 % 18 6 6 0 1.00
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/etag_with_template_digest.rb 64.71 % 55 17 11 6 0.65
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/flash.rb 69.57 % 61 23 16 7 0.87
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/head.rb 20.83 % 63 24 5 19 0.21
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/http_authentication.rb 40.00 % 520 145 58 87 0.40
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/implicit_render.rb 35.00 % 63 20 7 13 0.35
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/instrumentation.rb 77.78 % 106 45 35 10 0.80
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/live.rb 30.83 % 310 120 37 83 0.31
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/logging.rb 83.33 % 20 6 5 1 0.83
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/mime_responds.rb 34.38 % 328 64 22 42 0.34
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/parameter_encoding.rb 75.00 % 82 16 12 4 0.75
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/params_wrapper.rb 50.46 % 302 109 55 54 0.89
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/permissions_policy.rb 50.00 % 46 10 5 5 0.50
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/redirecting.rb 39.39 % 133 33 13 20 0.39
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/renderers.rb 72.73 % 181 55 40 15 0.89
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/rendering.rb 76.19 % 127 63 48 15 0.90
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/request_forgery_protection.rb 48.95 % 498 190 93 97 0.49
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/rescue.rb 72.73 % 28 11 8 3 0.73
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/streaming.rb 41.18 % 222 17 7 10 0.41
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/testing.rb 62.50 % 16 8 5 3 0.63
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/url_for.rb 29.41 % 58 17 5 12 0.29
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/renderer.rb 77.50 % 141 40 31 9 3.78
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/template_assertions.rb 75.00 % 11 4 3 1 0.75
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/test_case.rb 33.99 % 635 253 86 167 0.34
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/http/filter_redirect.rb 42.11 % 36 19 8 11 0.42
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/http/response.rb 77.47 % 540 253 196 57 1.15
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/middleware/flash.rb 42.64 % 300 129 55 74 0.43
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions.rb 76.92 % 24 13 10 3 0.77
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions/response.rb 35.14 % 104 37 13 24 0.35
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions/routing.rb 18.60 % 234 86 16 70 0.19
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/integration.rb 73.53 % 685 204 150 54 1.35
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/request_encoder.rb 79.31 % 55 29 23 6 0.90
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_process.rb 41.38 % 75 29 12 17 0.41
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_request.rb 54.29 % 71 35 19 16 0.54
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_response.rb 77.78 % 25 9 7 2 0.89
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/app/helpers/action_text/content_helper.rb 47.62 % 38 21 10 11 0.62
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/app/helpers/action_text/tag_helper.rb 50.00 % 87 32 16 16 0.53
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachment.rb 56.36 % 105 55 31 24 0.56
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/caching.rb 75.00 % 16 8 6 2 0.75
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/minification.rb 75.00 % 17 8 6 2 0.75
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/trix_conversion.rb 57.89 % 36 19 11 8 0.58
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/base.rb 52.63 % 281 76 40 36 0.53
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/context.rb 50.00 % 32 10 5 5 0.50
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags.rb 100.00 % 44 37 37 0 1.00
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags/base.rb 25.00 % 199 108 27 81 0.25
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags/placeholderable.rb 41.67 % 24 12 5 7 0.42
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/layouts.rb 50.60 % 431 83 42 41 0.80
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/log_subscriber.rb 33.87 % 111 62 21 41 0.34
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/lookup_context.rb 61.15 % 302 157 96 61 0.79
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/path_set.rb 59.52 % 91 42 25 17 2.40
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/rendering.rb 32.53 % 172 83 27 56 0.33
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/routing_url_for.rb 25.64 % 146 39 10 29 0.26
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template.rb 35.51 % 352 107 38 69 0.36
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers.rb 80.56 % 66 36 29 7 1.25
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/builder.rb 58.33 % 25 12 7 5 0.58
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/erb.rb 46.88 % 79 32 15 17 0.47
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/erb/erubi.rb 30.61 % 89 49 15 34 0.31
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/html.rb 80.00 % 11 5 4 1 0.80
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/raw.rb 80.00 % 11 5 4 1 0.80
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/resolver.rb 47.39 % 432 211 100 111 1.68
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/types.rb 72.41 % 57 29 21 8 0.79
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/test_case.rb 50.00 % 292 134 67 67 0.50
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/testing/resolvers.rb 50.00 % 45 22 11 11 0.50
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/view_paths.rb 90.70 % 126 43 39 4 2.33
vender/bundle/ruby/2.6.0/gems/activejob-6.1.3/lib/active_job/queue_adapters/test_adapter.rb 46.34 % 85 41 19 22 0.56
vender/bundle/ruby/2.6.0/gems/activejob-6.1.3/lib/active_job/test_helper.rb 34.15 % 705 164 56 108 0.79
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/alias_tracker.rb 52.38 % 84 42 22 20 1.95
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/association.rb 76.03 % 352 146 111 35 15.79
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/association_scope.rb 76.77 % 170 99 76 23 4.23
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/belongs_to_association.rb 63.77 % 138 69 44 25 18.68
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/collection_association.rb 36.64 % 515 232 85 147 0.96
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/foreign_association.rb 58.82 % 33 17 10 7 1.06
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/has_many_association.rb 38.24 % 157 68 26 42 0.62
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/has_many_through_association.rb 31.90 % 226 116 37 79 0.33
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/singular_association.rb 58.06 % 59 31 18 13 6.48
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/through_association.rb 58.82 % 121 51 30 21 0.75
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/connection_adapters/abstract/transaction.rb 80.20 % 370 202 162 40 47.22
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/file.rb 40.54 % 75 37 15 22 0.41
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/model_metadata.rb 56.25 % 32 16 9 7 0.56
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/render_context.rb 28.57 % 17 7 2 5 0.29
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/table_row.rb 32.05 % 152 78 25 53 0.32
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/table_rows.rb 55.00 % 46 20 11 9 0.55
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixtures.rb 44.91 % 784 167 75 92 1.16
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/internal_metadata.rb 51.72 % 62 29 15 14 0.52
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/null_relation.rb 51.52 % 67 33 17 16 0.52
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder.rb 62.22 % 169 90 56 34 13.87
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/array_handler.rb 42.31 % 48 26 11 15 1.08
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/association_query_value.rb 47.62 % 42 21 10 11 0.48
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/basic_object_handler.rb 100.00 % 19 10 10 0 4.80
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb 36.67 % 57 30 11 19 0.37
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/range_handler.rb 100.00 % 22 12 12 0 1.67
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/relation_handler.rb 44.44 % 19 9 4 5 0.44
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/result.rb 80.88 % 175 68 55 13 13.16
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/runtime_registry.rb 100.00 % 24 8 8 0 1.00
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/schema_migration.rb 64.00 % 54 25 16 9 0.76
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/statement_cache.rb 87.34 % 164 79 69 10 7.51
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/table_metadata.rb 78.26 % 81 46 36 10 9.89
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/test_fixtures.rb 53.38 % 246 133 71 62 6.05
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/configurable.rb 96.88 % 146 32 31 1 13.28
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/current_attributes.rb 50.00 % 208 58 29 29 0.60
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/current_attributes/test_helper.rb 42.86 % 13 7 3 4 0.43
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/test_case.rb 73.77 % 163 61 45 16 0.74
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/assertions.rb 18.97 % 235 58 11 47 0.19
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/constant_lookup.rb 57.14 % 51 14 8 6 0.57
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/declarative.rb 41.67 % 28 12 5 7 0.42
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/deprecation.rb 29.17 % 38 24 7 17 0.29
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/file_fixtures.rb 61.54 % 38 13 8 5 1.62
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/isolation.rb 24.14 % 110 58 14 44 0.24
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization.rb 62.07 % 51 29 18 11 0.62
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization/server.rb 35.71 % 78 42 15 27 0.36
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization/worker.rb 26.92 % 100 52 14 38 0.27
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/setup_and_teardown.rb 75.00 % 55 20 15 5 1.00
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/tagged_logging.rb 46.67 % 27 15 7 8 0.47
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/time_helpers.rb 37.70 % 235 61 23 38 0.38
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay.rb 75.00 % 284 40 30 10 1.48
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders.rb 100.00 % 18 5 5 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/_map.rb 100.00 % 17 3 3 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/encoder.rb 54.79 % 190 73 40 33 1.23
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/terminal.rb 84.78 % 195 46 39 7 4.70
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin.rb 66.67 % 55 18 12 6 1.17
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin_host.rb 79.73 % 221 74 59 15 5.27
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/word_list.rb 83.33 % 72 12 10 2 10.75
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners.rb 100.00 % 27 7 7 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/_map.rb 100.00 % 24 4 4 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby.rb 24.91 % 477 269 67 202 2.91
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/patterns.rb 100.00 % 178 45 45 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/string_state.rb 27.78 % 79 36 10 26 0.36
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/scanner.rb 69.37 % 337 111 77 34 2.14
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs.rb 23.85 % 739 260 62 198 0.25
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/backports.rb 25.00 % 9 4 1 3 0.25
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/block.rb 72.22 % 37 18 13 5 0.94
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/callbacks.rb 71.93 % 325 57 41 16 0.72
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/change.rb 51.72 % 174 87 45 42 0.66
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/hunk.rb 53.07 % 358 179 95 84 0.59
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/internals.rb 30.56 % 304 144 44 100 0.33
vender/bundle/ruby/2.6.0/gems/erubi-1.10.0/lib/erubi.rb 17.65 % 227 102 18 84 0.24
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/check_delivery_params.rb 31.03 % 60 29 9 20 0.31
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/file_delivery.rb 50.00 % 42 16 8 8 0.50
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/sendmail.rb 44.00 % 95 25 11 14 0.44
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/smtp.rb 30.56 % 149 36 11 25 0.31
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/test_mailer.rb 69.23 % 42 13 9 4 2.31
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest.rb 37.71 % 1056 411 155 256 0.44
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/assertions.rb 20.69 % 807 290 60 230 0.21
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/parallel.rb 57.14 % 70 28 16 12 0.57
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/test.rb 48.39 % 220 62 30 32 0.50
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/unit.rb 36.00 % 45 25 9 16 0.36
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/chunked.rb 41.46 % 117 41 17 24 0.41
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/mime.rb 72.73 % 685 11 8 3 1.00
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/mock.rb 60.80 % 273 125 76 49 1.24
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/response.rb 65.63 % 318 160 105 55 0.69
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb 39.02 % 523 246 96 150 0.39
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails-dom-testing.rb 100.00 % 1 1 1 0 1.00
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions.rb 100.00 % 18 11 11 0 1.00
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/dom_assertions.rb 38.89 % 76 36 14 22 0.39
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions.rb 30.51 % 302 59 18 41 0.31
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/count_describable.rb 55.00 % 32 20 11 9 0.55
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/html_selector.rb 21.54 % 114 65 14 51 0.23
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/substitution_context.rb 47.06 % 33 17 8 9 0.47
vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/base_formatter.rb 92.31 % 70 26 24 2 1.69
vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/base_text_formatter.rb 91.30 % 75 23 21 2 0.96
vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/progress_formatter.rb 100.00 % 29 15 15 0 2.27
vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/profiler.rb 100.00 % 34 19 19 0 8.53
vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/be.rb 56.41 % 191 78 44 34 0.64
vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/contain_exactly.rb 32.31 % 302 130 42 88 0.32
vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/eq.rb 86.67 % 40 15 13 2 1.47
vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/operators.rb 52.54 % 128 59 31 28 1.03
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails.rb 100.00 % 18 14 14 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/active_record.rb 72.73 % 25 11 8 3 0.73
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/adapters.rb 84.71 % 185 85 72 13 17.91
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/configuration.rb 83.54 % 194 79 66 13 1.95
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example.rb 100.00 % 13 13 13 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/channel_example_group.rb 60.61 % 93 33 20 13 0.61
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/controller_example_group.rb 34.62 % 216 78 27 51 0.35
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/feature_example_group.rb 62.50 % 53 24 15 9 0.63
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/helper_example_group.rb 66.67 % 42 21 14 7 0.67
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/job_example_group.rb 100.00 % 23 9 9 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/mailbox_example_group.rb 55.56 % 80 27 15 12 0.56
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/mailer_example_group.rb 76.47 % 38 17 13 4 0.76
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/model_example_group.rb 100.00 % 11 5 5 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/rails_example_group.rb 100.00 % 17 9 9 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/request_example_group.rb 100.00 % 30 17 17 0 1.35
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/routing_example_group.rb 70.83 % 63 24 17 7 0.71
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/system_example_group.rb 31.03 % 125 58 18 40 0.31
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/view_example_group.rb 35.16 % 213 91 32 59 0.35
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/extensions.rb 100.00 % 1 1 1 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/extensions/active_record/proxy.rb 100.00 % 11 4 4 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/file_fixture_support.rb 100.00 % 17 9 9 0 2.33
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/fixture_file_upload_support.rb 61.11 % 35 18 11 7 0.61
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/fixture_support.rb 69.44 % 64 36 25 11 3.89
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers.rb 100.00 % 35 22 22 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable.rb 69.23 % 65 13 9 4 0.69
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb 28.87 % 170 97 28 69 0.29
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable/have_streams.rb 54.17 % 58 24 13 11 0.54
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_mailbox.rb 57.14 % 64 28 16 12 0.57
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/active_job.rb 35.43 % 443 175 62 113 0.35
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/base_matcher.rb 49.25 % 179 67 33 34 0.49
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_a_new.rb 40.63 % 83 32 13 19 0.41
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_new_record.rb 66.67 % 30 12 8 4 0.67
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_valid.rb 61.90 % 49 21 13 8 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_enqueued_mail.rb 39.29 % 198 84 33 51 0.39
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_http_status.rb 48.39 % 385 124 60 64 0.48
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_rendered.rb 42.86 % 64 28 12 16 0.43
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/redirect_to.rb 58.82 % 38 17 10 7 0.59
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/relation_match_array.rb 100.00 % 3 2 2 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/routing_matchers.rb 48.89 % 125 45 22 23 0.62
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/vendor/capybara.rb 16.67 % 34 18 3 15 0.17
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_assigns.rb 61.54 % 45 13 8 5 0.62
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_path_builder.rb 62.50 % 29 8 5 3 0.63
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_rendering.rb 48.57 % 164 70 34 36 0.49
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_spec_methods.rb 35.29 % 56 17 6 11 0.35
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/differ.rb 73.64 % 215 110 81 29 0.91
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/hunk_generator.rb 100.00 % 47 24 24 0 1.25
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source.rb 97.67 % 85 43 42 1 36.47
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/location.rb 72.73 % 21 11 8 3 177.36
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/node.rb 98.00 % 110 50 49 1 544.14
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/token.rb 92.50 % 87 40 37 3 93.35

Controllers ( 14.81% covered at 0.15 hits/line )

5 files in total.
108 relevant lines, 16 lines covered and 92 lines missed. ( 14.81% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/controllers/api/v1/books_controller.rb 0.00 % 26 23 0 23 0.00
app/controllers/api/v1/categories_controller.rb 0.00 % 47 40 0 40 0.00
app/controllers/api/v1/formats_controller.rb 0.00 % 12 11 0 11 0.00
app/controllers/api/v1/problems_controller.rb 45.45 % 56 33 15 18 0.45
app/controllers/application_controller.rb 100.00 % 2 1 1 0 1.00

Channels ( 0.0% covered at 0.0 hits/line )

2 files in total.
8 relevant lines, 0 lines covered and 8 lines missed. ( 0.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/channels/application_cable/channel.rb 0.00 % 4 4 0 4 0.00
app/channels/application_cable/connection.rb 0.00 % 4 4 0 4 0.00

Models ( 58.33% covered at 0.93 hits/line )

10 files in total.
96 relevant lines, 56 lines covered and 40 lines missed. ( 58.33% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/models/application_record.rb 0.00 % 3 3 0 3 0.00
app/models/author.rb 100.00 % 6 3 3 0 1.00
app/models/authors_book.rb 100.00 % 2 1 1 0 1.00
app/models/book.rb 100.00 % 11 6 6 0 1.00
app/models/books_problem.rb 100.00 % 2 1 1 0 1.00
app/models/category.rb 83.78 % 56 37 31 6 1.59
app/models/category_relationship.rb 0.00 % 4 4 0 4 0.00
app/models/format.rb 100.00 % 12 4 4 0 1.00
app/models/period.rb 100.00 % 21 10 10 0 1.50
app/models/problem.rb 0.00 % 31 27 0 27 0.00

Mailers ( 0.0% covered at 0.0 hits/line )

1 files in total.
4 relevant lines, 0 lines covered and 4 lines missed. ( 0.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/mailers/application_mailer.rb 0.00 % 4 4 0 4 0.00

Helpers ( 49.06% covered at 0.57 hits/line )

2 files in total.
53 relevant lines, 26 lines covered and 27 lines missed. ( 49.06% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/app/helpers/action_text/content_helper.rb 47.62 % 38 21 10 11 0.62
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/app/helpers/action_text/tag_helper.rb 50.00 % 87 32 16 16 0.53

Jobs ( 0.0% covered at 0.0 hits/line )

1 files in total.
2 relevant lines, 0 lines covered and 2 lines missed. ( 0.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/jobs/application_job.rb 0.00 % 7 2 0 2 0.00

Libraries ( 51.28% covered at 4.81 hits/line )

258 files in total.
13547 relevant lines, 6947 lines covered and 6600 lines missed. ( 51.28% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
lib/capistrano/tasks/unicorn.rb 0.00 % 65 48 0 48 0.00
vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/channel/test_case.rb 52.75 % 310 91 48 43 0.53
vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/connection.rb 100.00 % 22 16 16 0 1.00
vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/connection/test_case.rb 50.63 % 234 79 40 39 0.51
vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/test_helper.rb 28.21 % 133 39 11 28 0.28
vender/bundle/ruby/2.6.0/gems/actionmailbox-6.1.3/lib/action_mailbox/test_helper.rb 52.94 % 96 17 9 8 0.53
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/base.rb 40.83 % 1031 218 89 129 0.41
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/collector.rb 52.38 % 32 21 11 10 0.52
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/delivery_job.rb 61.11 % 45 18 11 7 0.61
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/delivery_methods.rb 67.65 % 82 34 23 11 0.94
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/inline_preview_interceptor.rb 54.17 % 57 24 13 11 0.54
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/log_subscriber.rb 38.89 % 39 18 7 11 0.39
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/mail_delivery_job.rb 47.37 % 43 19 9 10 0.47
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/mail_helper.rb 30.43 % 72 23 7 16 0.30
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/message_delivery.rb 37.78 % 178 45 17 28 0.38
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/parameterized.rb 52.27 % 172 44 23 21 0.52
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/preview.rb 55.93 % 142 59 33 26 0.56
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/rescuable.rb 64.29 % 29 14 9 5 0.64
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/test_case.rb 59.42 % 121 69 41 28 0.86
vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/test_helper.rb 42.31 % 162 26 11 15 0.42
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/asset_paths.rb 100.00 % 12 5 5 0 1.20
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/base.rb 77.78 % 299 90 70 20 12.18
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/caching.rb 80.56 % 66 36 29 7 1.08
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/caching/fragments.rb 37.50 % 150 48 18 30 0.48
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/callbacks.rb 86.11 % 224 36 31 5 2.42
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/collector.rb 61.11 % 42 18 11 7 6.11
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/error.rb 100.00 % 6 2 2 0 1.00
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/logger.rb 100.00 % 14 7 7 0 1.57
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/rendering.rb 78.57 % 127 56 44 12 0.79
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/translation.rb 47.06 % 37 17 8 9 0.47
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/url_for.rb 84.62 % 35 13 11 2 0.85
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/api.rb 80.00 % 150 15 12 3 1.73
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/api/api_rendering.rb 75.00 % 16 8 6 2 0.75
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/base.rb 80.00 % 273 20 16 4 2.45
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/caching.rb 80.00 % 44 10 8 2 0.80
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/form_builder.rb 80.00 % 50 10 8 2 0.80
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/log_subscriber.rb 80.49 % 81 41 33 8 1.12
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal.rb 64.15 % 258 106 68 38 0.74
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/basic_implicit_render.rb 83.33 % 13 6 5 1 1.00
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/conditional_get.rb 48.65 % 288 37 18 19 0.54
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/content_security_policy.rb 55.56 % 51 27 15 12 0.56
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/cookies.rb 87.50 % 18 8 7 1 0.88
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/data_streaming.rb 32.43 % 151 37 12 25 0.32
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/default_headers.rb 100.00 % 17 7 7 0 1.00
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/etag_with_flash.rb 100.00 % 18 6 6 0 1.00
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/etag_with_template_digest.rb 64.71 % 55 17 11 6 0.65
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/flash.rb 69.57 % 61 23 16 7 0.87
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/head.rb 20.83 % 63 24 5 19 0.21
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/http_authentication.rb 40.00 % 520 145 58 87 0.40
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/implicit_render.rb 35.00 % 63 20 7 13 0.35
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/instrumentation.rb 77.78 % 106 45 35 10 0.80
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/live.rb 30.83 % 310 120 37 83 0.31
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/logging.rb 83.33 % 20 6 5 1 0.83
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/mime_responds.rb 34.38 % 328 64 22 42 0.34
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/parameter_encoding.rb 75.00 % 82 16 12 4 0.75
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/params_wrapper.rb 50.46 % 302 109 55 54 0.89
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/permissions_policy.rb 50.00 % 46 10 5 5 0.50
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/redirecting.rb 39.39 % 133 33 13 20 0.39
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/renderers.rb 72.73 % 181 55 40 15 0.89
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/rendering.rb 76.19 % 127 63 48 15 0.90
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/request_forgery_protection.rb 48.95 % 498 190 93 97 0.49
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/rescue.rb 72.73 % 28 11 8 3 0.73
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/streaming.rb 41.18 % 222 17 7 10 0.41
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/testing.rb 62.50 % 16 8 5 3 0.63
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/url_for.rb 29.41 % 58 17 5 12 0.29
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/renderer.rb 77.50 % 141 40 31 9 3.78
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/template_assertions.rb 75.00 % 11 4 3 1 0.75
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/test_case.rb 33.99 % 635 253 86 167 0.34
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/http/filter_redirect.rb 42.11 % 36 19 8 11 0.42
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/http/response.rb 77.47 % 540 253 196 57 1.15
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/middleware/flash.rb 42.64 % 300 129 55 74 0.43
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions.rb 76.92 % 24 13 10 3 0.77
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions/response.rb 35.14 % 104 37 13 24 0.35
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions/routing.rb 18.60 % 234 86 16 70 0.19
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/integration.rb 73.53 % 685 204 150 54 1.35
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/request_encoder.rb 79.31 % 55 29 23 6 0.90
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_process.rb 41.38 % 75 29 12 17 0.41
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_request.rb 54.29 % 71 35 19 16 0.54
vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_response.rb 77.78 % 25 9 7 2 0.89
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachment.rb 56.36 % 105 55 31 24 0.56
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/caching.rb 75.00 % 16 8 6 2 0.75
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/minification.rb 75.00 % 17 8 6 2 0.75
vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/trix_conversion.rb 57.89 % 36 19 11 8 0.58
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/base.rb 52.63 % 281 76 40 36 0.53
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/context.rb 50.00 % 32 10 5 5 0.50
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags.rb 100.00 % 44 37 37 0 1.00
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags/base.rb 25.00 % 199 108 27 81 0.25
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags/placeholderable.rb 41.67 % 24 12 5 7 0.42
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/layouts.rb 50.60 % 431 83 42 41 0.80
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/log_subscriber.rb 33.87 % 111 62 21 41 0.34
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/lookup_context.rb 61.15 % 302 157 96 61 0.79
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/path_set.rb 59.52 % 91 42 25 17 2.40
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/rendering.rb 32.53 % 172 83 27 56 0.33
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/routing_url_for.rb 25.64 % 146 39 10 29 0.26
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template.rb 35.51 % 352 107 38 69 0.36
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers.rb 80.56 % 66 36 29 7 1.25
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/builder.rb 58.33 % 25 12 7 5 0.58
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/erb.rb 46.88 % 79 32 15 17 0.47
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/erb/erubi.rb 30.61 % 89 49 15 34 0.31
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/html.rb 80.00 % 11 5 4 1 0.80
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/raw.rb 80.00 % 11 5 4 1 0.80
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/resolver.rb 47.39 % 432 211 100 111 1.68
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/types.rb 72.41 % 57 29 21 8 0.79
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/test_case.rb 50.00 % 292 134 67 67 0.50
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/testing/resolvers.rb 50.00 % 45 22 11 11 0.50
vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/view_paths.rb 90.70 % 126 43 39 4 2.33
vender/bundle/ruby/2.6.0/gems/activejob-6.1.3/lib/active_job/queue_adapters/test_adapter.rb 46.34 % 85 41 19 22 0.56
vender/bundle/ruby/2.6.0/gems/activejob-6.1.3/lib/active_job/test_helper.rb 34.15 % 705 164 56 108 0.79
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/alias_tracker.rb 52.38 % 84 42 22 20 1.95
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/association.rb 76.03 % 352 146 111 35 15.79
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/association_scope.rb 76.77 % 170 99 76 23 4.23
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/belongs_to_association.rb 63.77 % 138 69 44 25 18.68
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/collection_association.rb 36.64 % 515 232 85 147 0.96
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/foreign_association.rb 58.82 % 33 17 10 7 1.06
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/has_many_association.rb 38.24 % 157 68 26 42 0.62
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/has_many_through_association.rb 31.90 % 226 116 37 79 0.33
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/singular_association.rb 58.06 % 59 31 18 13 6.48
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/through_association.rb 58.82 % 121 51 30 21 0.75
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/connection_adapters/abstract/transaction.rb 80.20 % 370 202 162 40 47.22
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/file.rb 40.54 % 75 37 15 22 0.41
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/model_metadata.rb 56.25 % 32 16 9 7 0.56
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/render_context.rb 28.57 % 17 7 2 5 0.29
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/table_row.rb 32.05 % 152 78 25 53 0.32
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/table_rows.rb 55.00 % 46 20 11 9 0.55
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixtures.rb 44.91 % 784 167 75 92 1.16
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/internal_metadata.rb 51.72 % 62 29 15 14 0.52
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/null_relation.rb 51.52 % 67 33 17 16 0.52
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder.rb 62.22 % 169 90 56 34 13.87
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/array_handler.rb 42.31 % 48 26 11 15 1.08
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/association_query_value.rb 47.62 % 42 21 10 11 0.48
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/basic_object_handler.rb 100.00 % 19 10 10 0 4.80
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb 36.67 % 57 30 11 19 0.37
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/range_handler.rb 100.00 % 22 12 12 0 1.67
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/relation_handler.rb 44.44 % 19 9 4 5 0.44
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/result.rb 80.88 % 175 68 55 13 13.16
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/runtime_registry.rb 100.00 % 24 8 8 0 1.00
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/schema_migration.rb 64.00 % 54 25 16 9 0.76
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/statement_cache.rb 87.34 % 164 79 69 10 7.51
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/table_metadata.rb 78.26 % 81 46 36 10 9.89
vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/test_fixtures.rb 53.38 % 246 133 71 62 6.05
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/configurable.rb 96.88 % 146 32 31 1 13.28
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/current_attributes.rb 50.00 % 208 58 29 29 0.60
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/current_attributes/test_helper.rb 42.86 % 13 7 3 4 0.43
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/test_case.rb 73.77 % 163 61 45 16 0.74
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/assertions.rb 18.97 % 235 58 11 47 0.19
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/constant_lookup.rb 57.14 % 51 14 8 6 0.57
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/declarative.rb 41.67 % 28 12 5 7 0.42
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/deprecation.rb 29.17 % 38 24 7 17 0.29
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/file_fixtures.rb 61.54 % 38 13 8 5 1.62
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/isolation.rb 24.14 % 110 58 14 44 0.24
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization.rb 62.07 % 51 29 18 11 0.62
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization/server.rb 35.71 % 78 42 15 27 0.36
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization/worker.rb 26.92 % 100 52 14 38 0.27
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/setup_and_teardown.rb 75.00 % 55 20 15 5 1.00
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/tagged_logging.rb 46.67 % 27 15 7 8 0.47
vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/time_helpers.rb 37.70 % 235 61 23 38 0.38
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay.rb 75.00 % 284 40 30 10 1.48
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders.rb 100.00 % 18 5 5 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/_map.rb 100.00 % 17 3 3 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/encoder.rb 54.79 % 190 73 40 33 1.23
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/terminal.rb 84.78 % 195 46 39 7 4.70
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin.rb 66.67 % 55 18 12 6 1.17
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin_host.rb 79.73 % 221 74 59 15 5.27
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/word_list.rb 83.33 % 72 12 10 2 10.75
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners.rb 100.00 % 27 7 7 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/_map.rb 100.00 % 24 4 4 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby.rb 24.91 % 477 269 67 202 2.91
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/patterns.rb 100.00 % 178 45 45 0 1.00
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/string_state.rb 27.78 % 79 36 10 26 0.36
vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/scanner.rb 69.37 % 337 111 77 34 2.14
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs.rb 23.85 % 739 260 62 198 0.25
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/backports.rb 25.00 % 9 4 1 3 0.25
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/block.rb 72.22 % 37 18 13 5 0.94
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/callbacks.rb 71.93 % 325 57 41 16 0.72
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/change.rb 51.72 % 174 87 45 42 0.66
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/hunk.rb 53.07 % 358 179 95 84 0.59
vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/internals.rb 30.56 % 304 144 44 100 0.33
vender/bundle/ruby/2.6.0/gems/erubi-1.10.0/lib/erubi.rb 17.65 % 227 102 18 84 0.24
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/check_delivery_params.rb 31.03 % 60 29 9 20 0.31
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/file_delivery.rb 50.00 % 42 16 8 8 0.50
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/sendmail.rb 44.00 % 95 25 11 14 0.44
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/smtp.rb 30.56 % 149 36 11 25 0.31
vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/test_mailer.rb 69.23 % 42 13 9 4 2.31
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest.rb 37.71 % 1056 411 155 256 0.44
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/assertions.rb 20.69 % 807 290 60 230 0.21
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/parallel.rb 57.14 % 70 28 16 12 0.57
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/test.rb 48.39 % 220 62 30 32 0.50
vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/unit.rb 36.00 % 45 25 9 16 0.36
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/chunked.rb 41.46 % 117 41 17 24 0.41
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/mime.rb 72.73 % 685 11 8 3 1.00
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/mock.rb 60.80 % 273 125 76 49 1.24
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/response.rb 65.63 % 318 160 105 55 0.69
vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb 39.02 % 523 246 96 150 0.39
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails-dom-testing.rb 100.00 % 1 1 1 0 1.00
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions.rb 100.00 % 18 11 11 0 1.00
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/dom_assertions.rb 38.89 % 76 36 14 22 0.39
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions.rb 30.51 % 302 59 18 41 0.31
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/count_describable.rb 55.00 % 32 20 11 9 0.55
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/html_selector.rb 21.54 % 114 65 14 51 0.23
vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/substitution_context.rb 47.06 % 33 17 8 9 0.47
vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/base_formatter.rb 92.31 % 70 26 24 2 1.69
vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/base_text_formatter.rb 91.30 % 75 23 21 2 0.96
vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/progress_formatter.rb 100.00 % 29 15 15 0 2.27
vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/profiler.rb 100.00 % 34 19 19 0 8.53
vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/be.rb 56.41 % 191 78 44 34 0.64
vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/contain_exactly.rb 32.31 % 302 130 42 88 0.32
vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/eq.rb 86.67 % 40 15 13 2 1.47
vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/operators.rb 52.54 % 128 59 31 28 1.03
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails.rb 100.00 % 18 14 14 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/active_record.rb 72.73 % 25 11 8 3 0.73
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/adapters.rb 84.71 % 185 85 72 13 17.91
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/configuration.rb 83.54 % 194 79 66 13 1.95
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example.rb 100.00 % 13 13 13 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/channel_example_group.rb 60.61 % 93 33 20 13 0.61
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/controller_example_group.rb 34.62 % 216 78 27 51 0.35
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/feature_example_group.rb 62.50 % 53 24 15 9 0.63
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/helper_example_group.rb 66.67 % 42 21 14 7 0.67
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/job_example_group.rb 100.00 % 23 9 9 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/mailbox_example_group.rb 55.56 % 80 27 15 12 0.56
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/mailer_example_group.rb 76.47 % 38 17 13 4 0.76
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/model_example_group.rb 100.00 % 11 5 5 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/rails_example_group.rb 100.00 % 17 9 9 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/request_example_group.rb 100.00 % 30 17 17 0 1.35
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/routing_example_group.rb 70.83 % 63 24 17 7 0.71
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/system_example_group.rb 31.03 % 125 58 18 40 0.31
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/view_example_group.rb 35.16 % 213 91 32 59 0.35
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/extensions.rb 100.00 % 1 1 1 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/extensions/active_record/proxy.rb 100.00 % 11 4 4 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/file_fixture_support.rb 100.00 % 17 9 9 0 2.33
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/fixture_file_upload_support.rb 61.11 % 35 18 11 7 0.61
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/fixture_support.rb 69.44 % 64 36 25 11 3.89
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers.rb 100.00 % 35 22 22 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable.rb 69.23 % 65 13 9 4 0.69
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb 28.87 % 170 97 28 69 0.29
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable/have_streams.rb 54.17 % 58 24 13 11 0.54
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_mailbox.rb 57.14 % 64 28 16 12 0.57
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/active_job.rb 35.43 % 443 175 62 113 0.35
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/base_matcher.rb 49.25 % 179 67 33 34 0.49
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_a_new.rb 40.63 % 83 32 13 19 0.41
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_new_record.rb 66.67 % 30 12 8 4 0.67
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_valid.rb 61.90 % 49 21 13 8 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_enqueued_mail.rb 39.29 % 198 84 33 51 0.39
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_http_status.rb 48.39 % 385 124 60 64 0.48
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_rendered.rb 42.86 % 64 28 12 16 0.43
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/redirect_to.rb 58.82 % 38 17 10 7 0.59
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/relation_match_array.rb 100.00 % 3 2 2 0 1.00
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/routing_matchers.rb 48.89 % 125 45 22 23 0.62
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/vendor/capybara.rb 16.67 % 34 18 3 15 0.17
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_assigns.rb 61.54 % 45 13 8 5 0.62
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_path_builder.rb 62.50 % 29 8 5 3 0.63
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_rendering.rb 48.57 % 164 70 34 36 0.49
vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_spec_methods.rb 35.29 % 56 17 6 11 0.35
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/differ.rb 73.64 % 215 110 81 29 0.91
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/hunk_generator.rb 100.00 % 47 24 24 0 1.25
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source.rb 97.67 % 85 43 42 1 36.47
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/location.rb 72.73 % 21 11 8 3 177.36
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/node.rb 98.00 % 110 50 49 1 544.14
vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/token.rb 92.50 % 87 40 37 3 93.35

app/channels/application_cable/channel.rb

0.0% lines covered

4 relevant lines. 0 lines covered and 4 lines missed.
    
  1. module ApplicationCable
  2. class Channel < ActionCable::Channel::Base
  3. end
  4. end

app/channels/application_cable/connection.rb

0.0% lines covered

4 relevant lines. 0 lines covered and 4 lines missed.
    
  1. module ApplicationCable
  2. class Connection < ActionCable::Connection::Base
  3. end
  4. end

app/controllers/api/v1/books_controller.rb

0.0% lines covered

23 relevant lines. 0 lines covered and 23 lines missed.
    
  1. require "#{Rails.root}/app/controllers/application_controller.rb"
  2. module Api
  3. module V1
  4. class BooksController < ApplicationController
  5. def index
  6. books = Book.select(:id,:title)
  7. render json: books, status: :ok and return
  8. end
  9. def create
  10. book = Book.new(book_params)
  11. if book.save
  12. render json: book, status: :ok and return
  13. else
  14. render json: book.errors, status: :unprocessable_entity
  15. end
  16. end
  17. private
  18. def book_params
  19. params.require(:book).permit(:title,:price,:published_at,{categories:[]})
  20. end
  21. end
  22. end
  23. end

app/controllers/api/v1/categories_controller.rb

0.0% lines covered

40 relevant lines. 0 lines covered and 40 lines missed.
    
  1. require "#{Rails.root}/app/controllers/application_controller.rb"
  2. module Api
  3. module V1
  4. class CategoriesController < ApplicationController
  5. before_action :set_category, only: [:show, :update, :destroy]
  6. def index
  7. root_categories = Category.where(root:true).select(:id,:title)
  8. render json: root_categories, status: :created and return
  9. end
  10. def show
  11. if Category.find_by(id: params[:id]).nil?
  12. render status: :unprocessable_entity and return
  13. else
  14. render json: Category.relation_to_json(params[:id]), status: :ok and return
  15. end
  16. end
  17. def create
  18. if Category.json_to_relation(params[:category][:data].as_json)[0].kind_of?(Integer)
  19. render status: :created and return
  20. else
  21. render status: :unprocessable_entity
  22. end
  23. end
  24. def update
  25. if category.update(category_params)
  26. render json: category and return
  27. else
  28. render json: category.errors, status: :unprocessable_entity
  29. end
  30. end
  31. private
  32. def set_category
  33. category = Category.find(params[:id])
  34. end
  35. def category_params
  36. params.require(:category).permit(:name)
  37. end
  38. end
  39. end
  40. end

app/controllers/api/v1/formats_controller.rb

0.0% lines covered

11 relevant lines. 0 lines covered and 11 lines missed.
    
  1. require "#{Rails.root}/app/controllers/application_controller.rb"
  2. module Api
  3. module V1
  4. class FormatsController < ApplicationController
  5. def index
  6. formats = Format.select(:id,:problem_type)
  7. render json: formats, status: :ok and return
  8. end
  9. end
  10. end
  11. end

app/controllers/api/v1/problems_controller.rb

45.45% lines covered

33 relevant lines. 15 lines covered and 18 lines missed.
    
  1. 1 require "#{Rails.root}/app/controllers/application_controller.rb"
  2. 1 module Api
  3. 1 module V1
  4. 1 class ProblemsController < ApplicationController
  5. 1 before_action :set_problem, only: [:show, :update, :destroy]
  6. 1 def index
  7. 1 problems = Problem.all.order(id:'desc')
  8. 1 render json: problems, status: :ok and return
  9. end
  10. 1 def questions
  11. questions = Problem.of_questioin_select
  12. render json: questions, status: :ok and return
  13. end
  14. 1 def analyze
  15. binding.pry
  16. params[:problem].each do |problem|
  17. problem = Problem.find(problem["problem_id"])
  18. problem.calc_question_at(problem["correct"],problem["timesec"])
  19. end
  20. end
  21. 1 def create
  22. tmp_params = problem_params;
  23. tmp_params[:question_at] = Date.current + 1.days;
  24. tmp_params[:answers] = params[:problem]["answers"].as_json.map{|a|a["answer"]}.compact
  25. problem = Problem.new(tmp_params)
  26. if problem.save!
  27. render json: problem, status: :created and return
  28. else
  29. render json: problem.errors, status: :unprocessable_entity
  30. end
  31. end
  32. 1 def update
  33. if problem.update(problem_params)
  34. render json: problem, status: :created and return
  35. else
  36. render json: problem.errors, status: :unprocessable_entity
  37. end
  38. end
  39. 1 private
  40. 1 def set_problem
  41. problem = Problem.find(params[:id])
  42. end
  43. 1 def problem_params
  44. params.require(:problem).permit(:body,:question_at,:period_id,:format_id,{categories:[]},{answers:[]})
  45. end
  46. end
  47. end
  48. end

app/controllers/application_controller.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 class ApplicationController < ActionController::API
  2. end

app/jobs/application_job.rb

0.0% lines covered

2 relevant lines. 0 lines covered and 2 lines missed.
    
  1. class ApplicationJob < ActiveJob::Base
  2. # Automatically retry jobs that encountered a deadlock
  3. # retry_on ActiveRecord::Deadlocked
  4. # Most jobs are safe to ignore if the underlying records are no longer available
  5. # discard_on ActiveJob::DeserializationError
  6. end

app/mailers/application_mailer.rb

0.0% lines covered

4 relevant lines. 0 lines covered and 4 lines missed.
    
  1. class ApplicationMailer < ActionMailer::Base
  2. default from: 'from@example.com'
  3. layout 'mailer'
  4. end

app/models/application_record.rb

0.0% lines covered

3 relevant lines. 0 lines covered and 3 lines missed.
    
  1. class ApplicationRecord < ActiveRecord::Base
  2. self.abstract_class = true
  3. end

app/models/author.rb

100.0% lines covered

3 relevant lines. 3 lines covered and 0 lines missed.
    
  1. 1 class Author < ApplicationRecord
  2. 1 validates :name,
  3. presence: true
  4. 1 has_and_belongs_to_many :books
  5. end

app/models/authors_book.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 class AuthorsBook < ApplicationRecord
  2. end

app/models/book.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. 1 class Book < ApplicationRecord
  2. 1 validates :price,
  3. presence: true
  4. 1 validates :categories,
  5. presence: true
  6. 1 validates :published_at,
  7. presence: true
  8. 1 has_and_belongs_to_many :problems
  9. 1 has_and_belongs_to_many :authors
  10. end

app/models/books_problem.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 class BooksProblem < ApplicationRecord
  2. end

app/models/category.rb

83.78% lines covered

37 relevant lines. 31 lines covered and 6 lines missed.
    
  1. 1 class Category < ApplicationRecord
  2. 1 validates :title,
  3. presence: true
  4. 1 validates :key,
  5. presence: true
  6. 1 has_and_belongs_to_many :books
  7. 1 has_many :category_relationships
  8. 1 has_many :children, through: :category_relationships, source: :child
  9. 1 has_many :reverses_of_relation, class_name: 'CategoryRelationship', foreign_key: 'child_id'
  10. 1 has_many :parents, through: :reverses_of_relation, source: :category
  11. #function
  12. 1 def self.json_to_relation(json_data)
  13. 3 return false if json_data.blank?
  14. 3 nodes = []
  15. 3 json_data.each do |node|
  16. 4 record = {title:node["title"],key:node["key"],root:false}
  17. 4 if node["key"] == '0-0'
  18. 1 record[:root] = true
  19. end
  20. 4 if node.has_key?("children")
  21. 2 children = json_to_relation(node["children"])
  22. 2 category = Category.new(record)
  23. 2 raise Exception.new("children save error") unless category.save
  24. 2 children.each do |node_id|
  25. 3 category.category_relationships.find_or_create_by(child_id: node_id)
  26. end
  27. else
  28. 2 category = Category.new(record)
  29. 2 raise Exception.new("leaf node save error") unless category.save!
  30. end
  31. 4 nodes.push(category.id)
  32. end
  33. 3 nodes
  34. end
  35. 1 def self.relation_to_json(root_id)
  36. 1 root_node = Category.find_by(id: root_id)
  37. 1 tree_convert_loop(root_node)
  38. end
  39. 1 def self.tree_convert_loop(node,result=[])
  40. 1 if node.children.blank?
  41. result << {value:node.id,label:node.title}
  42. result
  43. else
  44. tmp_arr = []
  45. node.children.each do |node|
  46. tmp_arr = tree_convert_loop(node,tmp_arr)
  47. end
  48. result << {value:node.id,label:node.title,children:tmp_arr}
  49. end
  50. end
  51. 1 private_class_method :tree_convert_loop
  52. end

app/models/category_relationship.rb

0.0% lines covered

4 relevant lines. 0 lines covered and 4 lines missed.
    
  1. class CategoryRelationship < ApplicationRecord
  2. belongs_to :category
  3. belongs_to :child, class_name: 'Category'
  4. end

app/models/format.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. 1 class Format < ApplicationRecord
  2. 1 validates :problem_type,
  3. presence: true
  4. 1 enum problem_type: {
  5. sentence_problem: 0,
  6. blank_problem: 1,
  7. image_problem: 2,
  8. reordering_problem: 3
  9. }
  10. 1 has_many :problems
  11. end

app/models/period.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. 1 class Period < ApplicationRecord
  2. 1 has_many :problems
  3. #function
  4. 1 def next
  5. 2 Period.find_by(id: self.id+1) == nil ? self.period : Period.find_by(id: self.id+1).period
  6. end
  7. 1 def next_period_id
  8. 2 Period.find_by(id: self.id+1) == nil ? self.id : self.id+1
  9. end
  10. 1 def prev
  11. 2 self.id-1 < 0 ? self.period : Period.find_by(id: self.id-1).period
  12. end
  13. 1 def prev_period_id
  14. 3 self.id < 1 ? self.id : self.id-1
  15. end
  16. end

app/models/problem.rb

0.0% lines covered

27 relevant lines. 0 lines covered and 27 lines missed.
    
  1. class Problem < ApplicationRecord
  2. validates :body,
  3. presence: true
  4. validates :categories,
  5. presence: true
  6. belongs_to :format
  7. belongs_to :period
  8. has_and_belongs_to_many :books
  9. # function
  10. def self.of_questioin_select
  11. Problem.where(question_at:(Date.current.beginning_of_month)..)
  12. end
  13. def calc_question_at(correct_wrong, answer_speed)
  14. if answer_speed < 600
  15. if correct_wrong
  16. self.question_at += self.period.next.days
  17. self.period_id = self.period.next_period_id
  18. else
  19. self.question_at += self.period.prev.days
  20. self.period_id = self.period.prev_period_id
  21. end
  22. else
  23. self.question_at += self.period.period.days
  24. self.period_id = self.period.prev_period_id
  25. end
  26. self.save
  27. end
  28. end

lib/capistrano/tasks/unicorn.rb

0.0% lines covered

48 relevant lines. 0 lines covered and 48 lines missed.
    
  1. #unicornのpidファイル、設定ファイルのディレクトリを指定
  2. namespace :unicorn do
  3. task :environment do
  4. set :unicorn_pid, "#{current_path}/tmp/pids/unicorn.pid"
  5. set :unicorn_config, "#{current_path}/config/unicorn/production.rb"
  6. end
  7. #unicornをスタートさせるメソッド
  8. def start_unicorn
  9. within current_path do
  10. execute :bundle, :exec, :unicorn, "-c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D"
  11. end
  12. end
  13. #unicornを停止させるメソッド
  14. def stop_unicorn
  15. execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})"
  16. end
  17. #unicornを再起動するメソッド
  18. def reload_unicorn
  19. execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})"
  20. end
  21. #unicronを強制終了するメソッド
  22. def force_stop_unicorn
  23. execute :kill, "$(< #{fetch(:unicorn_pid)})"
  24. end
  25. #unicornをスタートさせるtask
  26. desc "Start unicorn server"
  27. task start: :environment do
  28. on roles(:app) do
  29. start_unicorn
  30. end
  31. end
  32. #unicornを停止させるtask
  33. desc "Stop unicorn server gracefully"
  34. task stop: :environment do
  35. on roles(:app) do
  36. stop_unicorn
  37. end
  38. end
  39. #既にunicornが起動している場合再起動を、まだの場合起動を行うtask
  40. desc "Restart unicorn server gracefully"
  41. task restart: :environment do
  42. on roles(:app) do
  43. if test("[ -f #{fetch(:unicorn_pid)} ]")
  44. reload_unicorn
  45. else
  46. start_unicorn
  47. end
  48. end
  49. end
  50. #unicornを強制終了させるtask
  51. desc "Stop unicorn server immediately"
  52. task force_stop: :environment do
  53. on roles(:app) do
  54. force_stop_unicorn
  55. end
  56. end
  57. end

vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/channel/test_case.rb

52.75% lines covered

91 relevant lines. 48 lines covered and 43 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support"
  3. 1 require "active_support/test_case"
  4. 1 require "active_support/core_ext/hash/indifferent_access"
  5. 1 require "json"
  6. 1 module ActionCable
  7. 1 module Channel
  8. 1 class NonInferrableChannelError < ::StandardError
  9. 1 def initialize(name)
  10. super "Unable to determine the channel to test from #{name}. " +
  11. "You'll need to specify it using `tests YourChannel` in your " +
  12. "test case definition."
  13. end
  14. end
  15. # Stub +stream_from+ to track streams for the channel.
  16. # Add public aliases for +subscription_confirmation_sent?+ and
  17. # +subscription_rejected?+.
  18. 1 module ChannelStub
  19. 1 def confirmed?
  20. subscription_confirmation_sent?
  21. end
  22. 1 def rejected?
  23. subscription_rejected?
  24. end
  25. 1 def stream_from(broadcasting, *)
  26. streams << broadcasting
  27. end
  28. 1 def stop_all_streams
  29. @_streams = []
  30. end
  31. 1 def streams
  32. @_streams ||= []
  33. end
  34. # Make periodic timers no-op
  35. 1 def start_periodic_timers; end
  36. 1 alias stop_periodic_timers start_periodic_timers
  37. end
  38. 1 class ConnectionStub
  39. 1 attr_reader :transmissions, :identifiers, :subscriptions, :logger
  40. 1 def initialize(identifiers = {})
  41. @transmissions = []
  42. identifiers.each do |identifier, val|
  43. define_singleton_method(identifier) { val }
  44. end
  45. @subscriptions = ActionCable::Connection::Subscriptions.new(self)
  46. @identifiers = identifiers.keys
  47. @logger = ActiveSupport::TaggedLogging.new ActiveSupport::Logger.new(StringIO.new)
  48. end
  49. 1 def transmit(cable_message)
  50. transmissions << cable_message.with_indifferent_access
  51. end
  52. end
  53. # Superclass for Action Cable channel functional tests.
  54. #
  55. # == Basic example
  56. #
  57. # Functional tests are written as follows:
  58. # 1. First, one uses the +subscribe+ method to simulate subscription creation.
  59. # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
  60. # transmitted messages, subscribed streams, etc.
  61. #
  62. # For example:
  63. #
  64. # class ChatChannelTest < ActionCable::Channel::TestCase
  65. # def test_subscribed_with_room_number
  66. # # Simulate a subscription creation
  67. # subscribe room_number: 1
  68. #
  69. # # Asserts that the subscription was successfully created
  70. # assert subscription.confirmed?
  71. #
  72. # # Asserts that the channel subscribes connection to a stream
  73. # assert_has_stream "chat_1"
  74. #
  75. # # Asserts that the channel subscribes connection to a specific
  76. # # stream created for a model
  77. # assert_has_stream_for Room.find(1)
  78. # end
  79. #
  80. # def test_does_not_stream_with_incorrect_room_number
  81. # subscribe room_number: -1
  82. #
  83. # # Asserts that not streams was started
  84. # assert_no_streams
  85. # end
  86. #
  87. # def test_does_not_subscribe_without_room_number
  88. # subscribe
  89. #
  90. # # Asserts that the subscription was rejected
  91. # assert subscription.rejected?
  92. # end
  93. # end
  94. #
  95. # You can also perform actions:
  96. # def test_perform_speak
  97. # subscribe room_number: 1
  98. #
  99. # perform :speak, message: "Hello, Rails!"
  100. #
  101. # assert_equal "Hello, Rails!", transmissions.last["text"]
  102. # end
  103. #
  104. # == Special methods
  105. #
  106. # ActionCable::Channel::TestCase will also automatically provide the following instance
  107. # methods for use in the tests:
  108. #
  109. # <b>connection</b>::
  110. # An ActionCable::Channel::ConnectionStub, representing the current HTTP connection.
  111. # <b>subscription</b>::
  112. # An instance of the current channel, created when you call +subscribe+.
  113. # <b>transmissions</b>::
  114. # A list of all messages that have been transmitted into the channel.
  115. #
  116. #
  117. # == Channel is automatically inferred
  118. #
  119. # ActionCable::Channel::TestCase will automatically infer the channel under test
  120. # from the test class name. If the channel cannot be inferred from the test
  121. # class name, you can explicitly set it with +tests+.
  122. #
  123. # class SpecialEdgeCaseChannelTest < ActionCable::Channel::TestCase
  124. # tests SpecialChannel
  125. # end
  126. #
  127. # == Specifying connection identifiers
  128. #
  129. # You need to set up your connection manually to provide values for the identifiers.
  130. # To do this just use:
  131. #
  132. # stub_connection(user: users(:john))
  133. #
  134. # == Testing broadcasting
  135. #
  136. # ActionCable::Channel::TestCase enhances ActionCable::TestHelper assertions (e.g.
  137. # +assert_broadcasts+) to handle broadcasting to models:
  138. #
  139. #
  140. # # in your channel
  141. # def speak(data)
  142. # broadcast_to room, text: data["message"]
  143. # end
  144. #
  145. # def test_speak
  146. # subscribe room_id: rooms(:chat).id
  147. #
  148. # assert_broadcast_on(rooms(:chat), text: "Hello, Rails!") do
  149. # perform :speak, message: "Hello, Rails!"
  150. # end
  151. # end
  152. 1 class TestCase < ActiveSupport::TestCase
  153. 1 module Behavior
  154. 1 extend ActiveSupport::Concern
  155. 1 include ActiveSupport::Testing::ConstantLookup
  156. 1 include ActionCable::TestHelper
  157. 1 CHANNEL_IDENTIFIER = "test_stub"
  158. 1 included do
  159. 1 class_attribute :_channel_class
  160. 1 attr_reader :connection, :subscription
  161. 1 ActiveSupport.run_load_hooks(:action_cable_channel_test_case, self)
  162. end
  163. 1 module ClassMethods
  164. 1 def tests(channel)
  165. case channel
  166. when String, Symbol
  167. self._channel_class = channel.to_s.camelize.constantize
  168. when Module
  169. self._channel_class = channel
  170. else
  171. raise NonInferrableChannelError.new(channel)
  172. end
  173. end
  174. 1 def channel_class
  175. if channel = self._channel_class
  176. channel
  177. else
  178. tests determine_default_channel(name)
  179. end
  180. end
  181. 1 def determine_default_channel(name)
  182. channel = determine_constant_from_test_name(name) do |constant|
  183. Class === constant && constant < ActionCable::Channel::Base
  184. end
  185. raise NonInferrableChannelError.new(name) if channel.nil?
  186. channel
  187. end
  188. end
  189. # Set up test connection with the specified identifiers:
  190. #
  191. # class ApplicationCable < ActionCable::Connection::Base
  192. # identified_by :user, :token
  193. # end
  194. #
  195. # stub_connection(user: users[:john], token: 'my-secret-token')
  196. 1 def stub_connection(identifiers = {})
  197. @connection = ConnectionStub.new(identifiers)
  198. end
  199. # Subscribe to the channel under test. Optionally pass subscription parameters as a Hash.
  200. 1 def subscribe(params = {})
  201. @connection ||= stub_connection
  202. @subscription = self.class.channel_class.new(connection, CHANNEL_IDENTIFIER, params.with_indifferent_access)
  203. @subscription.singleton_class.include(ChannelStub)
  204. @subscription.subscribe_to_channel
  205. @subscription
  206. end
  207. # Unsubscribe the subscription under test.
  208. 1 def unsubscribe
  209. check_subscribed!
  210. subscription.unsubscribe_from_channel
  211. end
  212. # Perform action on a channel.
  213. #
  214. # NOTE: Must be subscribed.
  215. 1 def perform(action, data = {})
  216. check_subscribed!
  217. subscription.perform_action(data.stringify_keys.merge("action" => action.to_s))
  218. end
  219. # Returns messages transmitted into channel
  220. 1 def transmissions
  221. # Return only directly sent message (via #transmit)
  222. connection.transmissions.map { |data| data["message"] }.compact
  223. end
  224. # Enhance TestHelper assertions to handle non-String
  225. # broadcastings
  226. 1 def assert_broadcasts(stream_or_object, *args)
  227. super(broadcasting_for(stream_or_object), *args)
  228. end
  229. 1 def assert_broadcast_on(stream_or_object, *args)
  230. super(broadcasting_for(stream_or_object), *args)
  231. end
  232. # Asserts that no streams have been started.
  233. #
  234. # def test_assert_no_started_stream
  235. # subscribe
  236. # assert_no_streams
  237. # end
  238. #
  239. 1 def assert_no_streams
  240. assert subscription.streams.empty?, "No streams started was expected, but #{subscription.streams.count} found"
  241. end
  242. # Asserts that the specified stream has been started.
  243. #
  244. # def test_assert_started_stream
  245. # subscribe
  246. # assert_has_stream 'messages'
  247. # end
  248. #
  249. 1 def assert_has_stream(stream)
  250. assert subscription.streams.include?(stream), "Stream #{stream} has not been started"
  251. end
  252. # Asserts that the specified stream for a model has started.
  253. #
  254. # def test_assert_started_stream_for
  255. # subscribe id: 42
  256. # assert_has_stream_for User.find(42)
  257. # end
  258. #
  259. 1 def assert_has_stream_for(object)
  260. assert_has_stream(broadcasting_for(object))
  261. end
  262. 1 private
  263. 1 def check_subscribed!
  264. raise "Must be subscribed!" if subscription.nil? || subscription.rejected?
  265. end
  266. 1 def broadcasting_for(stream_or_object)
  267. return stream_or_object if stream_or_object.is_a?(String)
  268. self.class.channel_class.broadcasting_for(stream_or_object)
  269. end
  270. end
  271. 1 include Behavior
  272. end
  273. end
  274. end

vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/connection.rb

100.0% lines covered

16 relevant lines. 16 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionCable
  3. 1 module Connection
  4. 1 extend ActiveSupport::Autoload
  5. 1 eager_autoload do
  6. 1 autoload :Authorization
  7. 1 autoload :Base
  8. 1 autoload :ClientSocket
  9. 1 autoload :Identification
  10. 1 autoload :InternalChannel
  11. 1 autoload :MessageBuffer
  12. 1 autoload :Stream
  13. 1 autoload :StreamEventLoop
  14. 1 autoload :Subscriptions
  15. 1 autoload :TaggedLoggerProxy
  16. 1 autoload :TestCase
  17. 1 autoload :WebSocket
  18. end
  19. end
  20. end

vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/connection/test_case.rb

50.63% lines covered

79 relevant lines. 40 lines covered and 39 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support"
  3. 1 require "active_support/test_case"
  4. 1 require "active_support/core_ext/hash/indifferent_access"
  5. 1 require "action_dispatch"
  6. 1 require "action_dispatch/http/headers"
  7. 1 require "action_dispatch/testing/test_request"
  8. 1 module ActionCable
  9. 1 module Connection
  10. 1 class NonInferrableConnectionError < ::StandardError
  11. 1 def initialize(name)
  12. super "Unable to determine the connection to test from #{name}. " +
  13. "You'll need to specify it using `tests YourConnection` in your " +
  14. "test case definition."
  15. end
  16. end
  17. 1 module Assertions
  18. # Asserts that the connection is rejected (via +reject_unauthorized_connection+).
  19. #
  20. # # Asserts that connection without user_id fails
  21. # assert_reject_connection { connect params: { user_id: '' } }
  22. 1 def assert_reject_connection(&block)
  23. assert_raises(Authorization::UnauthorizedError, "Expected to reject connection but no rejection was made", &block)
  24. end
  25. end
  26. # We don't want to use the whole "encryption stack" for connection
  27. # unit-tests, but we want to make sure that users test against the correct types
  28. # of cookies (i.e. signed or encrypted or plain)
  29. 1 class TestCookieJar < ActiveSupport::HashWithIndifferentAccess
  30. 1 def signed
  31. self[:signed] ||= {}.with_indifferent_access
  32. end
  33. 1 def encrypted
  34. self[:encrypted] ||= {}.with_indifferent_access
  35. end
  36. end
  37. 1 class TestRequest < ActionDispatch::TestRequest
  38. 1 attr_accessor :session, :cookie_jar
  39. end
  40. 1 module TestConnection
  41. 1 attr_reader :logger, :request
  42. 1 def initialize(request)
  43. inner_logger = ActiveSupport::Logger.new(StringIO.new)
  44. tagged_logging = ActiveSupport::TaggedLogging.new(inner_logger)
  45. @logger = ActionCable::Connection::TaggedLoggerProxy.new(tagged_logging, tags: [])
  46. @request = request
  47. @env = request.env
  48. end
  49. end
  50. # Unit test Action Cable connections.
  51. #
  52. # Useful to check whether a connection's +identified_by+ gets assigned properly
  53. # and that any improper connection requests are rejected.
  54. #
  55. # == Basic example
  56. #
  57. # Unit tests are written as follows:
  58. #
  59. # 1. Simulate a connection attempt by calling +connect+.
  60. # 2. Assert state, e.g. identifiers, has been assigned.
  61. #
  62. #
  63. # class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase
  64. # def test_connects_with_proper_cookie
  65. # # Simulate the connection request with a cookie.
  66. # cookies["user_id"] = users(:john).id
  67. #
  68. # connect
  69. #
  70. # # Assert the connection identifier matches the fixture.
  71. # assert_equal users(:john).id, connection.user.id
  72. # end
  73. #
  74. # def test_rejects_connection_without_proper_cookie
  75. # assert_reject_connection { connect }
  76. # end
  77. # end
  78. #
  79. # +connect+ accepts additional information about the HTTP request with the
  80. # +params+, +headers+, +session+ and Rack +env+ options.
  81. #
  82. # def test_connect_with_headers_and_query_string
  83. # connect params: { user_id: 1 }, headers: { "X-API-TOKEN" => "secret-my" }
  84. #
  85. # assert_equal "1", connection.user.id
  86. # assert_equal "secret-my", connection.token
  87. # end
  88. #
  89. # def test_connect_with_params
  90. # connect params: { user_id: 1 }
  91. #
  92. # assert_equal "1", connection.user.id
  93. # end
  94. #
  95. # You can also set up the correct cookies before the connection request:
  96. #
  97. # def test_connect_with_cookies
  98. # # Plain cookies:
  99. # cookies["user_id"] = 1
  100. #
  101. # # Or signed/encrypted:
  102. # # cookies.signed["user_id"] = 1
  103. # # cookies.encrypted["user_id"] = 1
  104. #
  105. # connect
  106. #
  107. # assert_equal "1", connection.user_id
  108. # end
  109. #
  110. # == Connection is automatically inferred
  111. #
  112. # ActionCable::Connection::TestCase will automatically infer the connection under test
  113. # from the test class name. If the channel cannot be inferred from the test
  114. # class name, you can explicitly set it with +tests+.
  115. #
  116. # class ConnectionTest < ActionCable::Connection::TestCase
  117. # tests ApplicationCable::Connection
  118. # end
  119. #
  120. 1 class TestCase < ActiveSupport::TestCase
  121. 1 module Behavior
  122. 1 extend ActiveSupport::Concern
  123. 1 DEFAULT_PATH = "/cable"
  124. 1 include ActiveSupport::Testing::ConstantLookup
  125. 1 include Assertions
  126. 1 included do
  127. 1 class_attribute :_connection_class
  128. 1 attr_reader :connection
  129. 1 ActiveSupport.run_load_hooks(:action_cable_connection_test_case, self)
  130. end
  131. 1 module ClassMethods
  132. 1 def tests(connection)
  133. case connection
  134. when String, Symbol
  135. self._connection_class = connection.to_s.camelize.constantize
  136. when Module
  137. self._connection_class = connection
  138. else
  139. raise NonInferrableConnectionError.new(connection)
  140. end
  141. end
  142. 1 def connection_class
  143. if connection = self._connection_class
  144. connection
  145. else
  146. tests determine_default_connection(name)
  147. end
  148. end
  149. 1 def determine_default_connection(name)
  150. connection = determine_constant_from_test_name(name) do |constant|
  151. Class === constant && constant < ActionCable::Connection::Base
  152. end
  153. raise NonInferrableConnectionError.new(name) if connection.nil?
  154. connection
  155. end
  156. end
  157. # Performs connection attempt to exert #connect on the connection under test.
  158. #
  159. # Accepts request path as the first argument and the following request options:
  160. #
  161. # - params – URL parameters (Hash)
  162. # - headers – request headers (Hash)
  163. # - session – session data (Hash)
  164. # - env – additional Rack env configuration (Hash)
  165. 1 def connect(path = ActionCable.server.config.mount_path, **request_params)
  166. path ||= DEFAULT_PATH
  167. connection = self.class.connection_class.allocate
  168. connection.singleton_class.include(TestConnection)
  169. connection.send(:initialize, build_test_request(path, **request_params))
  170. connection.connect if connection.respond_to?(:connect)
  171. # Only set instance variable if connected successfully
  172. @connection = connection
  173. end
  174. # Exert #disconnect on the connection under test.
  175. 1 def disconnect
  176. raise "Must be connected!" if connection.nil?
  177. connection.disconnect if connection.respond_to?(:disconnect)
  178. @connection = nil
  179. end
  180. 1 def cookies
  181. @cookie_jar ||= TestCookieJar.new
  182. end
  183. 1 private
  184. 1 def build_test_request(path, params: nil, headers: {}, session: {}, env: {})
  185. wrapped_headers = ActionDispatch::Http::Headers.from_hash(headers)
  186. uri = URI.parse(path)
  187. query_string = params.nil? ? uri.query : params.to_query
  188. request_env = {
  189. "QUERY_STRING" => query_string,
  190. "PATH_INFO" => uri.path
  191. }.merge(env)
  192. if wrapped_headers.present?
  193. ActionDispatch::Http::Headers.from_hash(request_env).merge!(wrapped_headers)
  194. end
  195. TestRequest.create(request_env).tap do |request|
  196. request.session = session.with_indifferent_access
  197. request.cookie_jar = cookies
  198. end
  199. end
  200. end
  201. 1 include Behavior
  202. end
  203. end
  204. end

vender/bundle/ruby/2.6.0/gems/actioncable-6.1.3/lib/action_cable/test_helper.rb

28.21% lines covered

39 relevant lines. 11 lines covered and 28 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionCable
  3. # Provides helper methods for testing Action Cable broadcasting
  4. 1 module TestHelper
  5. 1 def before_setup # :nodoc:
  6. server = ActionCable.server
  7. test_adapter = ActionCable::SubscriptionAdapter::Test.new(server)
  8. @old_pubsub_adapter = server.pubsub
  9. server.instance_variable_set(:@pubsub, test_adapter)
  10. super
  11. end
  12. 1 def after_teardown # :nodoc:
  13. super
  14. ActionCable.server.instance_variable_set(:@pubsub, @old_pubsub_adapter)
  15. end
  16. # Asserts that the number of broadcasted messages to the stream matches the given number.
  17. #
  18. # def test_broadcasts
  19. # assert_broadcasts 'messages', 0
  20. # ActionCable.server.broadcast 'messages', { text: 'hello' }
  21. # assert_broadcasts 'messages', 1
  22. # ActionCable.server.broadcast 'messages', { text: 'world' }
  23. # assert_broadcasts 'messages', 2
  24. # end
  25. #
  26. # If a block is passed, that block should cause the specified number of
  27. # messages to be broadcasted.
  28. #
  29. # def test_broadcasts_again
  30. # assert_broadcasts('messages', 1) do
  31. # ActionCable.server.broadcast 'messages', { text: 'hello' }
  32. # end
  33. #
  34. # assert_broadcasts('messages', 2) do
  35. # ActionCable.server.broadcast 'messages', { text: 'hi' }
  36. # ActionCable.server.broadcast 'messages', { text: 'how are you?' }
  37. # end
  38. # end
  39. #
  40. 1 def assert_broadcasts(stream, number, &block)
  41. if block_given?
  42. original_count = broadcasts_size(stream)
  43. assert_nothing_raised(&block)
  44. new_count = broadcasts_size(stream)
  45. actual_count = new_count - original_count
  46. else
  47. actual_count = broadcasts_size(stream)
  48. end
  49. assert_equal number, actual_count, "#{number} broadcasts to #{stream} expected, but #{actual_count} were sent"
  50. end
  51. # Asserts that no messages have been sent to the stream.
  52. #
  53. # def test_no_broadcasts
  54. # assert_no_broadcasts 'messages'
  55. # ActionCable.server.broadcast 'messages', { text: 'hi' }
  56. # assert_broadcasts 'messages', 1
  57. # end
  58. #
  59. # If a block is passed, that block should not cause any message to be sent.
  60. #
  61. # def test_broadcasts_again
  62. # assert_no_broadcasts 'messages' do
  63. # # No job messages should be sent from this block
  64. # end
  65. # end
  66. #
  67. # Note: This assertion is simply a shortcut for:
  68. #
  69. # assert_broadcasts 'messages', 0, &block
  70. #
  71. 1 def assert_no_broadcasts(stream, &block)
  72. assert_broadcasts stream, 0, &block
  73. end
  74. # Asserts that the specified message has been sent to the stream.
  75. #
  76. # def test_assert_transmitted_message
  77. # ActionCable.server.broadcast 'messages', text: 'hello'
  78. # assert_broadcast_on('messages', text: 'hello')
  79. # end
  80. #
  81. # If a block is passed, that block should cause a message with the specified data to be sent.
  82. #
  83. # def test_assert_broadcast_on_again
  84. # assert_broadcast_on('messages', text: 'hello') do
  85. # ActionCable.server.broadcast 'messages', text: 'hello'
  86. # end
  87. # end
  88. #
  89. 1 def assert_broadcast_on(stream, data, &block)
  90. # Encode to JSON and back–we want to use this value to compare
  91. # with decoded JSON.
  92. # Comparing JSON strings doesn't work due to the order if the keys.
  93. serialized_msg =
  94. ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(data))
  95. new_messages = broadcasts(stream)
  96. if block_given?
  97. old_messages = new_messages
  98. clear_messages(stream)
  99. assert_nothing_raised(&block)
  100. new_messages = broadcasts(stream)
  101. clear_messages(stream)
  102. # Restore all sent messages
  103. (old_messages + new_messages).each { |m| pubsub_adapter.broadcast(stream, m) }
  104. end
  105. message = new_messages.find { |msg| ActiveSupport::JSON.decode(msg) == serialized_msg }
  106. assert message, "No messages sent with #{data} to #{stream}"
  107. end
  108. 1 def pubsub_adapter # :nodoc:
  109. ActionCable.server.pubsub
  110. end
  111. 1 delegate :broadcasts, :clear_messages, to: :pubsub_adapter
  112. 1 private
  113. 1 def broadcasts_size(channel)
  114. broadcasts(channel).size
  115. end
  116. end
  117. end

vender/bundle/ruby/2.6.0/gems/actionmailbox-6.1.3/lib/action_mailbox/test_helper.rb

52.94% lines covered

17 relevant lines. 9 lines covered and 8 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "mail"
  3. 1 module ActionMailbox
  4. 1 module TestHelper
  5. # Create an +InboundEmail+ record using an eml fixture in the format of message/rfc822
  6. # referenced with +fixture_name+ located in +test/fixtures/files/fixture_name+.
  7. 1 def create_inbound_email_from_fixture(fixture_name, status: :processing)
  8. create_inbound_email_from_source file_fixture(fixture_name).read, status: status
  9. end
  10. # Creates an +InboundEmail+ by specifying through options or a block.
  11. #
  12. # ==== Options
  13. #
  14. # * <tt>:status</tt> - The +status+ to set for the created +InboundEmail+.
  15. # For possible statuses, see {its documentation}[rdoc-ref:ActionMailbox::InboundEmail].
  16. #
  17. # ==== Creating a simple email
  18. #
  19. # When you only need to set basic fields like +from+, +to+, +subject+, and
  20. # +body+, you can pass them directly as options.
  21. #
  22. # create_inbound_email_from_mail(from: "david@loudthinking.com", subject: "Hello!")
  23. #
  24. # ==== Creating a multi-part email
  25. #
  26. # When you need to create a more intricate email, like a multi-part email
  27. # that contains both a plaintext version and an HTML version, you can pass a
  28. # block.
  29. #
  30. # create_inbound_email_from_mail do
  31. # to "David Heinemeier Hansson <david@loudthinking.com>"
  32. # from "Bilbo Baggins <bilbo@bagend.com>"
  33. # subject "Come down to the Shire!"
  34. #
  35. # text_part do
  36. # body "Please join us for a party at Bag End"
  37. # end
  38. #
  39. # html_part do
  40. # body "<h1>Please join us for a party at Bag End</h1>"
  41. # end
  42. # end
  43. #
  44. # As with +Mail.new+, you can also use a block parameter to define the parts
  45. # of the message:
  46. #
  47. # create_inbound_email_from_mail do |mail|
  48. # mail.to "David Heinemeier Hansson <david@loudthinking.com>"
  49. # mail.from "Bilbo Baggins <bilbo@bagend.com>"
  50. # mail.subject "Come down to the Shire!"
  51. #
  52. # mail.text_part do |part|
  53. # part.body "Please join us for a party at Bag End"
  54. # end
  55. #
  56. # mail.html_part do |part|
  57. # part.body "<h1>Please join us for a party at Bag End</h1>"
  58. # end
  59. # end
  60. 1 def create_inbound_email_from_mail(status: :processing, **mail_options, &block)
  61. mail = Mail.new(mail_options, &block)
  62. # Bcc header is not encoded by default
  63. mail[:bcc].include_in_headers = true if mail[:bcc]
  64. create_inbound_email_from_source mail.to_s, status: status
  65. end
  66. # Create an +InboundEmail+ using the raw rfc822 +source+ as text.
  67. 1 def create_inbound_email_from_source(source, status: :processing)
  68. ActionMailbox::InboundEmail.create_and_extract_message_id! source, status: status
  69. end
  70. # Create an +InboundEmail+ from fixture using the same arguments as +create_inbound_email_from_fixture+
  71. # and immediately route it to processing.
  72. 1 def receive_inbound_email_from_fixture(*args)
  73. create_inbound_email_from_fixture(*args).tap(&:route)
  74. end
  75. # Create an +InboundEmail+ using the same options or block as
  76. # {create_inbound_email_from_mail}[rdoc-ref:#create_inbound_email_from_mail],
  77. # then immediately route it for processing.
  78. 1 def receive_inbound_email_from_mail(**kwargs, &block)
  79. create_inbound_email_from_mail(**kwargs, &block).tap(&:route)
  80. end
  81. # Create an +InboundEmail+ using the same arguments as +create_inbound_email_from_source+ and immediately route it
  82. # to processing.
  83. 1 def receive_inbound_email_from_source(*args)
  84. create_inbound_email_from_source(*args).tap(&:route)
  85. end
  86. end
  87. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/base.rb

40.83% lines covered

218 relevant lines. 89 lines covered and 129 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "mail"
  3. 1 require "action_mailer/collector"
  4. 1 require "active_support/core_ext/string/inflections"
  5. 1 require "active_support/core_ext/hash/except"
  6. 1 require "active_support/core_ext/module/anonymous"
  7. 1 require "action_mailer/log_subscriber"
  8. 1 require "action_mailer/rescuable"
  9. 1 module ActionMailer
  10. # Action Mailer allows you to send email from your application using a mailer model and views.
  11. #
  12. # = Mailer Models
  13. #
  14. # To use Action Mailer, you need to create a mailer model.
  15. #
  16. # $ bin/rails generate mailer Notifier
  17. #
  18. # The generated model inherits from <tt>ApplicationMailer</tt> which in turn
  19. # inherits from <tt>ActionMailer::Base</tt>. A mailer model defines methods
  20. # used to generate an email message. In these methods, you can set up variables to be used in
  21. # the mailer views, options on the mail itself such as the <tt>:from</tt> address, and attachments.
  22. #
  23. # class ApplicationMailer < ActionMailer::Base
  24. # default from: 'from@example.com'
  25. # layout 'mailer'
  26. # end
  27. #
  28. # class NotifierMailer < ApplicationMailer
  29. # default from: 'no-reply@example.com',
  30. # return_path: 'system@example.com'
  31. #
  32. # def welcome(recipient)
  33. # @account = recipient
  34. # mail(to: recipient.email_address_with_name,
  35. # bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
  36. # end
  37. # end
  38. #
  39. # Within the mailer method, you have access to the following methods:
  40. #
  41. # * <tt>attachments[]=</tt> - Allows you to add attachments to your email in an intuitive
  42. # manner; <tt>attachments['filename.png'] = File.read('path/to/filename.png')</tt>
  43. #
  44. # * <tt>attachments.inline[]=</tt> - Allows you to add an inline attachment to your email
  45. # in the same manner as <tt>attachments[]=</tt>
  46. #
  47. # * <tt>headers[]=</tt> - Allows you to specify any header field in your email such
  48. # as <tt>headers['X-No-Spam'] = 'True'</tt>. Note that declaring a header multiple times
  49. # will add many fields of the same name. Read #headers doc for more information.
  50. #
  51. # * <tt>headers(hash)</tt> - Allows you to specify multiple headers in your email such
  52. # as <tt>headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})</tt>
  53. #
  54. # * <tt>mail</tt> - Allows you to specify email to be sent.
  55. #
  56. # The hash passed to the mail method allows you to specify any header that a <tt>Mail::Message</tt>
  57. # will accept (any valid email header including optional fields).
  58. #
  59. # The +mail+ method, if not passed a block, will inspect your views and send all the views with
  60. # the same name as the method, so the above action would send the +welcome.text.erb+ view
  61. # file as well as the +welcome.html.erb+ view file in a +multipart/alternative+ email.
  62. #
  63. # If you want to explicitly render only certain templates, pass a block:
  64. #
  65. # mail(to: user.email) do |format|
  66. # format.text
  67. # format.html
  68. # end
  69. #
  70. # The block syntax is also useful in providing information specific to a part:
  71. #
  72. # mail(to: user.email) do |format|
  73. # format.text(content_transfer_encoding: "base64")
  74. # format.html
  75. # end
  76. #
  77. # Or even to render a special view:
  78. #
  79. # mail(to: user.email) do |format|
  80. # format.text
  81. # format.html { render "some_other_template" }
  82. # end
  83. #
  84. # = Mailer views
  85. #
  86. # Like Action Controller, each mailer class has a corresponding view directory in which each
  87. # method of the class looks for a template with its name.
  88. #
  89. # To define a template to be used with a mailer, create an <tt>.erb</tt> file with the same
  90. # name as the method in your mailer model. For example, in the mailer defined above, the template at
  91. # <tt>app/views/notifier_mailer/welcome.text.erb</tt> would be used to generate the email.
  92. #
  93. # Variables defined in the methods of your mailer model are accessible as instance variables in their
  94. # corresponding view.
  95. #
  96. # Emails by default are sent in plain text, so a sample view for our model example might look like this:
  97. #
  98. # Hi <%= @account.name %>,
  99. # Thanks for joining our service! Please check back often.
  100. #
  101. # You can even use Action View helpers in these views. For example:
  102. #
  103. # You got a new note!
  104. # <%= truncate(@note.body, length: 25) %>
  105. #
  106. # If you need to access the subject, from or the recipients in the view, you can do that through message object:
  107. #
  108. # You got a new note from <%= message.from %>!
  109. # <%= truncate(@note.body, length: 25) %>
  110. #
  111. #
  112. # = Generating URLs
  113. #
  114. # URLs can be generated in mailer views using <tt>url_for</tt> or named routes. Unlike controllers from
  115. # Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need
  116. # to provide all of the details needed to generate a URL.
  117. #
  118. # When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
  119. #
  120. # <%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
  121. #
  122. # When using named routes you only need to supply the <tt>:host</tt>:
  123. #
  124. # <%= users_url(host: "example.com") %>
  125. #
  126. # You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
  127. # <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
  128. # have no concept of a current URL from which to determine a relative path.
  129. #
  130. # It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
  131. # option as a configuration option in <tt>config/application.rb</tt>:
  132. #
  133. # config.action_mailer.default_url_options = { host: "example.com" }
  134. #
  135. # You can also define a <tt>default_url_options</tt> method on individual mailers to override these
  136. # default settings per-mailer.
  137. #
  138. # By default when <tt>config.force_ssl</tt> is +true+, URLs generated for hosts will use the HTTPS protocol.
  139. #
  140. # = Sending mail
  141. #
  142. # Once a mailer action and template are defined, you can deliver your message or defer its creation and
  143. # delivery for later:
  144. #
  145. # NotifierMailer.welcome(User.first).deliver_now # sends the email
  146. # mail = NotifierMailer.welcome(User.first) # => an ActionMailer::MessageDelivery object
  147. # mail.deliver_now # generates and sends the email now
  148. #
  149. # The <tt>ActionMailer::MessageDelivery</tt> class is a wrapper around a delegate that will call
  150. # your method to generate the mail. If you want direct access to the delegator, or <tt>Mail::Message</tt>,
  151. # you can call the <tt>message</tt> method on the <tt>ActionMailer::MessageDelivery</tt> object.
  152. #
  153. # NotifierMailer.welcome(User.first).message # => a Mail::Message object
  154. #
  155. # Action Mailer is nicely integrated with Active Job so you can generate and send emails in the background
  156. # (example: outside of the request-response cycle, so the user doesn't have to wait on it):
  157. #
  158. # NotifierMailer.welcome(User.first).deliver_later # enqueue the email sending to Active Job
  159. #
  160. # Note that <tt>deliver_later</tt> will execute your method from the background job.
  161. #
  162. # You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
  163. # All instance methods are expected to return a message object to be sent.
  164. #
  165. # = Multipart Emails
  166. #
  167. # Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
  168. # multipart templates, where each template is named after the name of the action, followed by the content
  169. # type. Each such detected template will be added to the message, as a separate part.
  170. #
  171. # For example, if the following templates exist:
  172. # * signup_notification.text.erb
  173. # * signup_notification.html.erb
  174. # * signup_notification.xml.builder
  175. # * signup_notification.yml.erb
  176. #
  177. # Each would be rendered and added as a separate part to the message, with the corresponding content
  178. # type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>,
  179. # which indicates that the email contains multiple different representations of the same email
  180. # body. The same instance variables defined in the action are passed to all email templates.
  181. #
  182. # Implicit template rendering is not performed if any attachments or parts have been added to the email.
  183. # This means that you'll have to manually add each part to the email and set the content type of the email
  184. # to <tt>multipart/alternative</tt>.
  185. #
  186. # = Attachments
  187. #
  188. # Sending attachment in emails is easy:
  189. #
  190. # class NotifierMailer < ApplicationMailer
  191. # def welcome(recipient)
  192. # attachments['free_book.pdf'] = File.read('path/to/file.pdf')
  193. # mail(to: recipient, subject: "New account information")
  194. # end
  195. # end
  196. #
  197. # Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.html.erb</tt>
  198. # template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
  199. # the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
  200. # and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
  201. # with the filename +free_book.pdf+.
  202. #
  203. # If you need to send attachments with no content, you need to create an empty view for it,
  204. # or add an empty body parameter like this:
  205. #
  206. # class NotifierMailer < ApplicationMailer
  207. # def welcome(recipient)
  208. # attachments['free_book.pdf'] = File.read('path/to/file.pdf')
  209. # mail(to: recipient, subject: "New account information", body: "")
  210. # end
  211. # end
  212. #
  213. # You can also send attachments with html template, in this case you need to add body, attachments,
  214. # and custom content type like this:
  215. #
  216. # class NotifierMailer < ApplicationMailer
  217. # def welcome(recipient)
  218. # attachments["free_book.pdf"] = File.read("path/to/file.pdf")
  219. # mail(to: recipient,
  220. # subject: "New account information",
  221. # content_type: "text/html",
  222. # body: "<html><body>Hello there</body></html>")
  223. # end
  224. # end
  225. #
  226. # = Inline Attachments
  227. #
  228. # You can also specify that a file should be displayed inline with other HTML. This is useful
  229. # if you want to display a corporate logo or a photo.
  230. #
  231. # class NotifierMailer < ApplicationMailer
  232. # def welcome(recipient)
  233. # attachments.inline['photo.png'] = File.read('path/to/photo.png')
  234. # mail(to: recipient, subject: "Here is what we look like")
  235. # end
  236. # end
  237. #
  238. # And then to reference the image in the view, you create a <tt>welcome.html.erb</tt> file and
  239. # make a call to +image_tag+ passing in the attachment you want to display and then call
  240. # +url+ on the attachment to get the relative content id path for the image source:
  241. #
  242. # <h1>Please Don't Cringe</h1>
  243. #
  244. # <%= image_tag attachments['photo.png'].url -%>
  245. #
  246. # As we are using Action View's +image_tag+ method, you can pass in any other options you want:
  247. #
  248. # <h1>Please Don't Cringe</h1>
  249. #
  250. # <%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
  251. #
  252. # = Observing and Intercepting Mails
  253. #
  254. # Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to
  255. # register classes that are called during the mail delivery life cycle.
  256. #
  257. # An observer class must implement the <tt>:delivered_email(message)</tt> method which will be
  258. # called once for every email sent after the email has been sent.
  259. #
  260. # An interceptor class must implement the <tt>:delivering_email(message)</tt> method which will be
  261. # called before the email is sent, allowing you to make modifications to the email before it hits
  262. # the delivery agents. Your class should make any needed modifications directly to the passed
  263. # in <tt>Mail::Message</tt> instance.
  264. #
  265. # = Default Hash
  266. #
  267. # Action Mailer provides some intelligent defaults for your emails, these are usually specified in a
  268. # default method inside the class definition:
  269. #
  270. # class NotifierMailer < ApplicationMailer
  271. # default sender: 'system@example.com'
  272. # end
  273. #
  274. # You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
  275. # <tt>ActionMailer::Base</tt> sets the following:
  276. #
  277. # * <tt>mime_version: "1.0"</tt>
  278. # * <tt>charset: "UTF-8"</tt>
  279. # * <tt>content_type: "text/plain"</tt>
  280. # * <tt>parts_order: [ "text/plain", "text/enriched", "text/html" ]</tt>
  281. #
  282. # <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
  283. # but Action Mailer translates them appropriately and sets the correct values.
  284. #
  285. # As you can pass in any header, you need to either quote the header as a string, or pass it in as
  286. # an underscored symbol, so the following will work:
  287. #
  288. # class NotifierMailer < ApplicationMailer
  289. # default 'Content-Transfer-Encoding' => '7bit',
  290. # content_description: 'This is a description'
  291. # end
  292. #
  293. # Finally, Action Mailer also supports passing <tt>Proc</tt> and <tt>Lambda</tt> objects into the default hash,
  294. # so you can define methods that evaluate as the message is being generated:
  295. #
  296. # class NotifierMailer < ApplicationMailer
  297. # default 'X-Special-Header' => Proc.new { my_method }, to: -> { @inviter.email_address }
  298. #
  299. # private
  300. # def my_method
  301. # 'some complex call'
  302. # end
  303. # end
  304. #
  305. # Note that the proc/lambda is evaluated right at the start of the mail message generation, so if you
  306. # set something in the default hash using a proc, and then set the same thing inside of your
  307. # mailer method, it will get overwritten by the mailer method.
  308. #
  309. # It is also possible to set these default options that will be used in all mailers through
  310. # the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
  311. #
  312. # config.action_mailer.default_options = { from: "no-reply@example.org" }
  313. #
  314. # = Callbacks
  315. #
  316. # You can specify callbacks using <tt>before_action</tt> and <tt>after_action</tt> for configuring your messages.
  317. # This may be useful, for example, when you want to add default inline attachments for all
  318. # messages sent out by a certain mailer class:
  319. #
  320. # class NotifierMailer < ApplicationMailer
  321. # before_action :add_inline_attachment!
  322. #
  323. # def welcome
  324. # mail
  325. # end
  326. #
  327. # private
  328. # def add_inline_attachment!
  329. # attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
  330. # end
  331. # end
  332. #
  333. # Callbacks in Action Mailer are implemented using
  334. # <tt>AbstractController::Callbacks</tt>, so you can define and configure
  335. # callbacks in the same manner that you would use callbacks in classes that
  336. # inherit from <tt>ActionController::Base</tt>.
  337. #
  338. # Note that unless you have a specific reason to do so, you should prefer
  339. # using <tt>before_action</tt> rather than <tt>after_action</tt> in your
  340. # Action Mailer classes so that headers are parsed properly.
  341. #
  342. # = Previewing emails
  343. #
  344. # You can preview your email templates visually by adding a mailer preview file to the
  345. # <tt>ActionMailer::Base.preview_path</tt>. Since most emails do something interesting
  346. # with database data, you'll need to write some scenarios to load messages with fake data:
  347. #
  348. # class NotifierMailerPreview < ActionMailer::Preview
  349. # def welcome
  350. # NotifierMailer.welcome(User.first)
  351. # end
  352. # end
  353. #
  354. # Methods must return a <tt>Mail::Message</tt> object which can be generated by calling the mailer
  355. # method without the additional <tt>deliver_now</tt> / <tt>deliver_later</tt>. The location of the
  356. # mailer previews directory can be configured using the <tt>preview_path</tt> option which has a default
  357. # of <tt>test/mailers/previews</tt>:
  358. #
  359. # config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
  360. #
  361. # An overview of all previews is accessible at <tt>http://localhost:3000/rails/mailers</tt>
  362. # on a running development server instance.
  363. #
  364. # Previews can also be intercepted in a similar manner as deliveries can be by registering
  365. # a preview interceptor that has a <tt>previewing_email</tt> method:
  366. #
  367. # class CssInlineStyler
  368. # def self.previewing_email(message)
  369. # # inline CSS styles
  370. # end
  371. # end
  372. #
  373. # config.action_mailer.preview_interceptors :css_inline_styler
  374. #
  375. # Note that interceptors need to be registered both with <tt>register_interceptor</tt>
  376. # and <tt>register_preview_interceptor</tt> if they should operate on both sending and
  377. # previewing emails.
  378. #
  379. # = Configuration options
  380. #
  381. # These options are specified on the class level, like
  382. # <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
  383. #
  384. # * <tt>default_options</tt> - You can pass this in at a class level as well as within the class itself as
  385. # per the above section.
  386. #
  387. # * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
  388. # Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and Log4r loggers.
  389. #
  390. # * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
  391. # * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default
  392. # "localhost" setting.
  393. # * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
  394. # * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
  395. # * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
  396. # * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
  397. # * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
  398. # authentication type here.
  399. # This is a symbol and one of <tt>:plain</tt> (will send the password Base64 encoded), <tt>:login</tt> (will
  400. # send the password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
  401. # information and a cryptographic Message Digest 5 algorithm to hash important information)
  402. # * <tt>:enable_starttls_auto</tt> - Detects if STARTTLS is enabled in your SMTP server and starts
  403. # to use it. Defaults to <tt>true</tt>.
  404. # * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
  405. # really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
  406. # of an OpenSSL verify constant (<tt>'none'</tt> or <tt>'peer'</tt>) or directly the constant
  407. # (<tt>OpenSSL::SSL::VERIFY_NONE</tt> or <tt>OpenSSL::SSL::VERIFY_PEER</tt>).
  408. # * <tt>:ssl/:tls</tt> Enables the SMTP connection to use SMTP/TLS (SMTPS: SMTP over direct TLS connection)
  409. #
  410. # * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
  411. # * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
  412. # * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i</tt> with <tt>-f sender@address</tt>
  413. # added automatically before the message is sent.
  414. #
  415. # * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
  416. # * <tt>:location</tt> - The directory into which emails will be written. Defaults to the application
  417. # <tt>tmp/mails</tt>.
  418. #
  419. # * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
  420. #
  421. # * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default),
  422. # <tt>:sendmail</tt>, <tt>:test</tt>, and <tt>:file</tt>. Or you may provide a custom delivery method
  423. # object e.g. +MyOwnDeliveryMethodClass+. See the Mail gem documentation on the interface you need to
  424. # implement for a custom delivery agent.
  425. #
  426. # * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you
  427. # call <tt>.deliver</tt> on an email message or on an Action Mailer method. This is on by default but can
  428. # be turned off to aid in functional testing.
  429. #
  430. # * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
  431. # <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
  432. #
  433. # * <tt>deliver_later_queue_name</tt> - The name of the queue used with <tt>deliver_later</tt>. Defaults to +mailers+.
  434. 1 class Base < AbstractController::Base
  435. 1 include DeliveryMethods
  436. 1 include Rescuable
  437. 1 include Parameterized
  438. 1 include Previews
  439. 1 abstract!
  440. 1 include AbstractController::Rendering
  441. 1 include AbstractController::Logger
  442. 1 include AbstractController::Helpers
  443. 1 include AbstractController::Translation
  444. 1 include AbstractController::AssetPaths
  445. 1 include AbstractController::Callbacks
  446. 1 include AbstractController::Caching
  447. 1 include ActionView::Layouts
  448. 1 PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]
  449. 1 helper ActionMailer::MailHelper
  450. 1 class_attribute :delivery_job, default: ::ActionMailer::DeliveryJob
  451. 1 class_attribute :default_params, default: {
  452. mime_version: "1.0",
  453. charset: "UTF-8",
  454. content_type: "text/plain",
  455. parts_order: [ "text/plain", "text/enriched", "text/html" ]
  456. }.freeze
  457. 1 class << self
  458. # Register one or more Observers which will be notified when mail is delivered.
  459. 1 def register_observers(*observers)
  460. 1 observers.flatten.compact.each { |observer| register_observer(observer) }
  461. end
  462. # Unregister one or more previously registered Observers.
  463. 1 def unregister_observers(*observers)
  464. observers.flatten.compact.each { |observer| unregister_observer(observer) }
  465. end
  466. # Register one or more Interceptors which will be called before mail is sent.
  467. 1 def register_interceptors(*interceptors)
  468. 1 interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
  469. end
  470. # Unregister one or more previously registered Interceptors.
  471. 1 def unregister_interceptors(*interceptors)
  472. interceptors.flatten.compact.each { |interceptor| unregister_interceptor(interceptor) }
  473. end
  474. # Register an Observer which will be notified when mail is delivered.
  475. # Either a class, string or symbol can be passed in as the Observer.
  476. # If a string or symbol is passed in it will be camelized and constantized.
  477. 1 def register_observer(observer)
  478. Mail.register_observer(observer_class_for(observer))
  479. end
  480. # Unregister a previously registered Observer.
  481. # Either a class, string or symbol can be passed in as the Observer.
  482. # If a string or symbol is passed in it will be camelized and constantized.
  483. 1 def unregister_observer(observer)
  484. Mail.unregister_observer(observer_class_for(observer))
  485. end
  486. # Register an Interceptor which will be called before mail is sent.
  487. # Either a class, string or symbol can be passed in as the Interceptor.
  488. # If a string or symbol is passed in it will be camelized and constantized.
  489. 1 def register_interceptor(interceptor)
  490. Mail.register_interceptor(observer_class_for(interceptor))
  491. end
  492. # Unregister a previously registered Interceptor.
  493. # Either a class, string or symbol can be passed in as the Interceptor.
  494. # If a string or symbol is passed in it will be camelized and constantized.
  495. 1 def unregister_interceptor(interceptor)
  496. Mail.unregister_interceptor(observer_class_for(interceptor))
  497. end
  498. 1 def observer_class_for(value) # :nodoc:
  499. case value
  500. when String, Symbol
  501. value.to_s.camelize.constantize
  502. else
  503. value
  504. end
  505. end
  506. 1 private :observer_class_for
  507. # Returns the name of the current mailer. This method is also being used as a path for a view lookup.
  508. # If this is an anonymous mailer, this method will return +anonymous+ instead.
  509. 1 def mailer_name
  510. @mailer_name ||= anonymous? ? "anonymous" : name.underscore
  511. end
  512. # Allows to set the name of current mailer.
  513. 1 attr_writer :mailer_name
  514. 1 alias :controller_path :mailer_name
  515. # Sets the defaults through app configuration:
  516. #
  517. # config.action_mailer.default(from: "no-reply@example.org")
  518. #
  519. # Aliased by ::default_options=
  520. 1 def default(value = nil)
  521. self.default_params = default_params.merge(value).freeze if value
  522. default_params
  523. end
  524. # Allows to set defaults through app configuration:
  525. #
  526. # config.action_mailer.default_options = { from: "no-reply@example.org" }
  527. 1 alias :default_options= :default
  528. # Wraps an email delivery inside of <tt>ActiveSupport::Notifications</tt> instrumentation.
  529. #
  530. # This method is actually called by the <tt>Mail::Message</tt> object itself
  531. # through a callback when you call <tt>:deliver</tt> on the <tt>Mail::Message</tt>,
  532. # calling +deliver_mail+ directly and passing a <tt>Mail::Message</tt> will do
  533. # nothing except tell the logger you sent the email.
  534. 1 def deliver_mail(mail) #:nodoc:
  535. ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
  536. set_payload_for_mail(payload, mail)
  537. yield # Let Mail do the delivery actions
  538. end
  539. end
  540. # Returns an email in the format "Name <email@example.com>".
  541. 1 def email_address_with_name(address, name)
  542. Mail::Address.new.tap do |builder|
  543. builder.address = address
  544. builder.display_name = name
  545. end.to_s
  546. end
  547. 1 private
  548. 1 def set_payload_for_mail(payload, mail)
  549. payload[:mail] = mail.encoded
  550. payload[:mailer] = name
  551. payload[:message_id] = mail.message_id
  552. payload[:subject] = mail.subject
  553. payload[:to] = mail.to
  554. payload[:from] = mail.from
  555. payload[:bcc] = mail.bcc if mail.bcc.present?
  556. payload[:cc] = mail.cc if mail.cc.present?
  557. payload[:date] = mail.date
  558. payload[:perform_deliveries] = mail.perform_deliveries
  559. end
  560. 1 def method_missing(method_name, *args)
  561. if action_methods.include?(method_name.to_s)
  562. MessageDelivery.new(self, method_name, *args)
  563. else
  564. super
  565. end
  566. end
  567. 1 ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
  568. 1 def respond_to_missing?(method, include_all = false)
  569. 1 action_methods.include?(method.to_s) || super
  570. end
  571. end
  572. 1 attr_internal :message
  573. 1 def initialize
  574. super()
  575. @_mail_was_called = false
  576. @_message = Mail.new
  577. end
  578. 1 def process(method_name, *args) #:nodoc:
  579. payload = {
  580. mailer: self.class.name,
  581. action: method_name,
  582. args: args
  583. }
  584. ActiveSupport::Notifications.instrument("process.action_mailer", payload) do
  585. super
  586. @_message = NullMail.new unless @_mail_was_called
  587. end
  588. end
  589. 1 class NullMail #:nodoc:
  590. 1 def body; "" end
  591. 1 def header; {} end
  592. 1 def respond_to?(string, include_all = false)
  593. true
  594. end
  595. 1 def method_missing(*args)
  596. nil
  597. end
  598. end
  599. # Returns the name of the mailer object.
  600. 1 def mailer_name
  601. self.class.mailer_name
  602. end
  603. # Returns an email in the format "Name <email@example.com>".
  604. 1 def email_address_with_name(address, name)
  605. self.class.email_address_with_name(address, name)
  606. end
  607. # Allows you to pass random and unusual headers to the new <tt>Mail::Message</tt>
  608. # object which will add them to itself.
  609. #
  610. # headers['X-Special-Domain-Specific-Header'] = "SecretValue"
  611. #
  612. # You can also pass a hash into headers of header field names and values,
  613. # which will then be set on the <tt>Mail::Message</tt> object:
  614. #
  615. # headers 'X-Special-Domain-Specific-Header' => "SecretValue",
  616. # 'In-Reply-To' => incoming.message_id
  617. #
  618. # The resulting <tt>Mail::Message</tt> will have the following in its header:
  619. #
  620. # X-Special-Domain-Specific-Header: SecretValue
  621. #
  622. # Note about replacing already defined headers:
  623. #
  624. # * +subject+
  625. # * +sender+
  626. # * +from+
  627. # * +to+
  628. # * +cc+
  629. # * +bcc+
  630. # * +reply-to+
  631. # * +orig-date+
  632. # * +message-id+
  633. # * +references+
  634. #
  635. # Fields can only appear once in email headers while other fields such as
  636. # <tt>X-Anything</tt> can appear multiple times.
  637. #
  638. # If you want to replace any header which already exists, first set it to
  639. # +nil+ in order to reset the value otherwise another field will be added
  640. # for the same header.
  641. 1 def headers(args = nil)
  642. if args
  643. @_message.headers(args)
  644. else
  645. @_message
  646. end
  647. end
  648. # Allows you to add attachments to an email, like so:
  649. #
  650. # mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
  651. #
  652. # If you do this, then Mail will take the file name and work out the mime type.
  653. # It will also set the Content-Type, Content-Disposition, Content-Transfer-Encoding
  654. # and encode the contents of the attachment in Base64.
  655. #
  656. # You can also specify overrides if you want by passing a hash instead of a string:
  657. #
  658. # mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
  659. # content: File.read('/path/to/filename.jpg')}
  660. #
  661. # If you want to use encoding other than Base64 then you will need to pass encoding
  662. # type along with the pre-encoded content as Mail doesn't know how to decode the
  663. # data:
  664. #
  665. # file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
  666. # mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
  667. # encoding: 'SpecialEncoding',
  668. # content: file_content }
  669. #
  670. # You can also search for specific attachments:
  671. #
  672. # # By Filename
  673. # mail.attachments['filename.jpg'] # => Mail::Part object or nil
  674. #
  675. # # or by index
  676. # mail.attachments[0] # => Mail::Part (first attachment)
  677. #
  678. 1 def attachments
  679. if @_mail_was_called
  680. LateAttachmentsProxy.new(@_message.attachments)
  681. else
  682. @_message.attachments
  683. end
  684. end
  685. 1 class LateAttachmentsProxy < SimpleDelegator
  686. 1 def inline; self end
  687. 1 def []=(_name, _content); _raise_error end
  688. 1 private
  689. 1 def _raise_error
  690. raise RuntimeError, "Can't add attachments after `mail` was called.\n" \
  691. "Make sure to use `attachments[]=` before calling `mail`."
  692. end
  693. end
  694. # The main method that creates the message and renders the email templates. There are
  695. # two ways to call this method, with a block, or without a block.
  696. #
  697. # It accepts a headers hash. This hash allows you to specify
  698. # the most used headers in an email message, these are:
  699. #
  700. # * +:subject+ - The subject of the message, if this is omitted, Action Mailer will
  701. # ask the Rails I18n class for a translated +:subject+ in the scope of
  702. # <tt>[mailer_scope, action_name]</tt> or if this is missing, will translate the
  703. # humanized version of the +action_name+
  704. # * +:to+ - Who the message is destined for, can be a string of addresses, or an array
  705. # of addresses.
  706. # * +:from+ - Who the message is from
  707. # * +:cc+ - Who you would like to Carbon-Copy on this email, can be a string of addresses,
  708. # or an array of addresses.
  709. # * +:bcc+ - Who you would like to Blind-Carbon-Copy on this email, can be a string of
  710. # addresses, or an array of addresses.
  711. # * +:reply_to+ - Who to set the Reply-To header of the email to.
  712. # * +:date+ - The date to say the email was sent on.
  713. #
  714. # You can set default values for any of the above headers (except +:date+)
  715. # by using the ::default class method:
  716. #
  717. # class Notifier < ActionMailer::Base
  718. # default from: 'no-reply@test.lindsaar.net',
  719. # bcc: 'email_logger@test.lindsaar.net',
  720. # reply_to: 'bounces@test.lindsaar.net'
  721. # end
  722. #
  723. # If you need other headers not listed above, you can either pass them in
  724. # as part of the headers hash or use the <tt>headers['name'] = value</tt>
  725. # method.
  726. #
  727. # When a +:return_path+ is specified as header, that value will be used as
  728. # the 'envelope from' address for the Mail message. Setting this is useful
  729. # when you want delivery notifications sent to a different address than the
  730. # one in +:from+. Mail will actually use the +:return_path+ in preference
  731. # to the +:sender+ in preference to the +:from+ field for the 'envelope
  732. # from' value.
  733. #
  734. # If you do not pass a block to the +mail+ method, it will find all
  735. # templates in the view paths using by default the mailer name and the
  736. # method name that it is being called from, it will then create parts for
  737. # each of these templates intelligently, making educated guesses on correct
  738. # content type and sequence, and return a fully prepared <tt>Mail::Message</tt>
  739. # ready to call <tt>:deliver</tt> on to send.
  740. #
  741. # For example:
  742. #
  743. # class Notifier < ActionMailer::Base
  744. # default from: 'no-reply@test.lindsaar.net'
  745. #
  746. # def welcome
  747. # mail(to: 'mikel@test.lindsaar.net')
  748. # end
  749. # end
  750. #
  751. # Will look for all templates at "app/views/notifier" with name "welcome".
  752. # If no welcome template exists, it will raise an ActionView::MissingTemplate error.
  753. #
  754. # However, those can be customized:
  755. #
  756. # mail(template_path: 'notifications', template_name: 'another')
  757. #
  758. # And now it will look for all templates at "app/views/notifications" with name "another".
  759. #
  760. # If you do pass a block, you can render specific templates of your choice:
  761. #
  762. # mail(to: 'mikel@test.lindsaar.net') do |format|
  763. # format.text
  764. # format.html
  765. # end
  766. #
  767. # You can even render plain text directly without using a template:
  768. #
  769. # mail(to: 'mikel@test.lindsaar.net') do |format|
  770. # format.text { render plain: "Hello Mikel!" }
  771. # format.html { render html: "<h1>Hello Mikel!</h1>".html_safe }
  772. # end
  773. #
  774. # Which will render a +multipart/alternative+ email with +text/plain+ and
  775. # +text/html+ parts.
  776. #
  777. # The block syntax also allows you to customize the part headers if desired:
  778. #
  779. # mail(to: 'mikel@test.lindsaar.net') do |format|
  780. # format.text(content_transfer_encoding: "base64")
  781. # format.html
  782. # end
  783. #
  784. 1 def mail(headers = {}, &block)
  785. return message if @_mail_was_called && headers.blank? && !block
  786. # At the beginning, do not consider class default for content_type
  787. content_type = headers[:content_type]
  788. headers = apply_defaults(headers)
  789. # Apply charset at the beginning so all fields are properly quoted
  790. message.charset = charset = headers[:charset]
  791. # Set configure delivery behavior
  792. wrap_delivery_behavior!(headers[:delivery_method], headers[:delivery_method_options])
  793. assign_headers_to_message(message, headers)
  794. # Render the templates and blocks
  795. responses = collect_responses(headers, &block)
  796. @_mail_was_called = true
  797. create_parts_from_responses(message, responses)
  798. wrap_inline_attachments(message)
  799. # Set up content type, reapply charset and handle parts order
  800. message.content_type = set_content_type(message, content_type, headers[:content_type])
  801. message.charset = charset
  802. if message.multipart?
  803. message.body.set_sort_order(headers[:parts_order])
  804. message.body.sort_parts!
  805. end
  806. message
  807. end
  808. 1 private
  809. # Used by #mail to set the content type of the message.
  810. #
  811. # It will use the given +user_content_type+, or multipart if the mail
  812. # message has any attachments. If the attachments are inline, the content
  813. # type will be "multipart/related", otherwise "multipart/mixed".
  814. #
  815. # If there is no content type passed in via headers, and there are no
  816. # attachments, or the message is multipart, then the default content type is
  817. # used.
  818. 1 def set_content_type(m, user_content_type, class_default) # :doc:
  819. params = m.content_type_parameters || {}
  820. case
  821. when user_content_type.present?
  822. user_content_type
  823. when m.has_attachments?
  824. if m.attachments.all?(&:inline?)
  825. ["multipart", "related", params]
  826. else
  827. ["multipart", "mixed", params]
  828. end
  829. when m.multipart?
  830. ["multipart", "alternative", params]
  831. else
  832. m.content_type || class_default
  833. end
  834. end
  835. # Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
  836. # If it does not find a translation for the +subject+ under the specified scope it will default to a
  837. # humanized version of the <tt>action_name</tt>.
  838. # If the subject has interpolations, you can pass them through the +interpolations+ parameter.
  839. 1 def default_i18n_subject(interpolations = {}) # :doc:
  840. mailer_scope = self.class.mailer_name.tr("/", ".")
  841. I18n.t(:subject, **interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
  842. end
  843. # Emails do not support relative path links.
  844. 1 def self.supports_path? # :doc:
  845. false
  846. end
  847. 1 def apply_defaults(headers)
  848. default_values = self.class.default.transform_values do |value|
  849. compute_default(value)
  850. end
  851. headers_with_defaults = headers.reverse_merge(default_values)
  852. headers_with_defaults[:subject] ||= default_i18n_subject
  853. headers_with_defaults
  854. end
  855. 1 def compute_default(value)
  856. return value unless value.is_a?(Proc)
  857. if value.arity == 1
  858. instance_exec(self, &value)
  859. else
  860. instance_exec(&value)
  861. end
  862. end
  863. 1 def assign_headers_to_message(message, headers)
  864. assignable = headers.except(:parts_order, :content_type, :body, :template_name,
  865. :template_path, :delivery_method, :delivery_method_options)
  866. assignable.each { |k, v| message[k] = v }
  867. end
  868. 1 def collect_responses(headers, &block)
  869. if block_given?
  870. collect_responses_from_block(headers, &block)
  871. elsif headers[:body]
  872. collect_responses_from_text(headers)
  873. else
  874. collect_responses_from_templates(headers)
  875. end
  876. end
  877. 1 def collect_responses_from_block(headers)
  878. templates_name = headers[:template_name] || action_name
  879. collector = ActionMailer::Collector.new(lookup_context) { render(templates_name) }
  880. yield(collector)
  881. collector.responses
  882. end
  883. 1 def collect_responses_from_text(headers)
  884. [{
  885. body: headers.delete(:body),
  886. content_type: headers[:content_type] || "text/plain"
  887. }]
  888. end
  889. 1 def collect_responses_from_templates(headers)
  890. templates_path = headers[:template_path] || self.class.mailer_name
  891. templates_name = headers[:template_name] || action_name
  892. each_template(Array(templates_path), templates_name).map do |template|
  893. format = template.format || self.formats.first
  894. {
  895. body: render(template: template, formats: [format]),
  896. content_type: Mime[format].to_s
  897. }
  898. end
  899. end
  900. 1 def each_template(paths, name, &block)
  901. templates = lookup_context.find_all(name, paths)
  902. if templates.empty?
  903. raise ActionView::MissingTemplate.new(paths, name, paths, false, "mailer")
  904. else
  905. templates.uniq(&:format).each(&block)
  906. end
  907. end
  908. 1 def wrap_inline_attachments(message)
  909. # If we have both types of attachment, wrap all the inline attachments
  910. # in multipart/related, but not the actual attachments
  911. if message.attachments.detect(&:inline?) && message.attachments.detect { |a| !a.inline? }
  912. related = Mail::Part.new
  913. related.content_type = "multipart/related"
  914. mixed = [ related ]
  915. message.parts.each do |p|
  916. if p.attachment? && !p.inline?
  917. mixed << p
  918. else
  919. related.add_part(p)
  920. end
  921. end
  922. message.parts.clear
  923. mixed.each { |c| message.add_part(c) }
  924. end
  925. end
  926. 1 def create_parts_from_responses(m, responses)
  927. if responses.size == 1 && !m.has_attachments?
  928. responses[0].each { |k, v| m[k] = v }
  929. elsif responses.size > 1 && m.has_attachments?
  930. container = Mail::Part.new
  931. container.content_type = "multipart/alternative"
  932. responses.each { |r| insert_part(container, r, m.charset) }
  933. m.add_part(container)
  934. else
  935. responses.each { |r| insert_part(m, r, m.charset) }
  936. end
  937. end
  938. 1 def insert_part(container, response, charset)
  939. response[:charset] ||= charset
  940. part = Mail::Part.new(response)
  941. container.add_part(part)
  942. end
  943. # This and #instrument_name is for caching instrument
  944. 1 def instrument_payload(key)
  945. {
  946. mailer: mailer_name,
  947. key: key
  948. }
  949. end
  950. 1 def instrument_name
  951. "action_mailer"
  952. end
  953. 1 def _protected_ivars
  954. PROTECTED_IVARS
  955. end
  956. 1 ActiveSupport.run_load_hooks(:action_mailer, self)
  957. end
  958. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/collector.rb

52.38% lines covered

21 relevant lines. 11 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "abstract_controller/collector"
  3. 1 require "active_support/core_ext/hash/reverse_merge"
  4. 1 require "active_support/core_ext/array/extract_options"
  5. 1 module ActionMailer
  6. 1 class Collector
  7. 1 include AbstractController::Collector
  8. 1 attr_reader :responses
  9. 1 def initialize(context, &block)
  10. @context = context
  11. @responses = []
  12. @default_render = block
  13. end
  14. 1 def any(*args, &block)
  15. options = args.extract_options!
  16. raise ArgumentError, "You have to supply at least one format" if args.empty?
  17. args.each { |type| send(type, options.dup, &block) }
  18. end
  19. 1 alias :all :any
  20. 1 def custom(mime, options = {})
  21. options.reverse_merge!(content_type: mime.to_s)
  22. @context.formats = [mime.to_sym]
  23. options[:body] = block_given? ? yield : @default_render.call
  24. @responses << options
  25. end
  26. end
  27. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/delivery_job.rb

61.11% lines covered

18 relevant lines. 11 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_job"
  3. 1 module ActionMailer
  4. # The <tt>ActionMailer::DeliveryJob</tt> class is used when you
  5. # want to send emails outside of the request-response cycle.
  6. #
  7. # Exceptions are rescued and handled by the mailer class.
  8. 1 class DeliveryJob < ActiveJob::Base # :nodoc:
  9. 1 queue_as { ActionMailer::Base.deliver_later_queue_name }
  10. 1 rescue_from StandardError, with: :handle_exception_with_mailer_class
  11. 1 before_perform do
  12. ActiveSupport::Deprecation.warn <<~MSG.squish
  13. Sending mail with DeliveryJob and Parameterized::DeliveryJob
  14. is deprecated and will be removed in Rails 6.2.
  15. Please use MailDeliveryJob instead.
  16. MSG
  17. end
  18. 1 def perform(mailer, mail_method, delivery_method, *args) #:nodoc:
  19. mailer.constantize.public_send(mail_method, *args).send(delivery_method)
  20. end
  21. 1 ruby2_keywords(:perform) if respond_to?(:ruby2_keywords, true)
  22. 1 private
  23. # "Deserialize" the mailer class name by hand in case another argument
  24. # (like a Global ID reference) raised DeserializationError.
  25. 1 def mailer_class
  26. if mailer = Array(@serialized_arguments).first || Array(arguments).first
  27. mailer.constantize
  28. end
  29. end
  30. 1 def handle_exception_with_mailer_class(exception)
  31. if klass = mailer_class
  32. klass.handle_exception exception
  33. else
  34. raise exception
  35. end
  36. end
  37. end
  38. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/delivery_methods.rb

67.65% lines covered

34 relevant lines. 23 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "tmpdir"
  3. 1 module ActionMailer
  4. # This module handles everything related to mail delivery, from registering
  5. # new delivery methods to configuring the mail object to be sent.
  6. 1 module DeliveryMethods
  7. 1 extend ActiveSupport::Concern
  8. 1 included do
  9. # Do not make this inheritable, because we always want it to propagate
  10. 1 cattr_accessor :raise_delivery_errors, default: true
  11. 1 cattr_accessor :perform_deliveries, default: true
  12. 1 cattr_accessor :deliver_later_queue_name, default: :mailers
  13. 1 class_attribute :delivery_methods, default: {}.freeze
  14. 1 class_attribute :delivery_method, default: :smtp
  15. 1 add_delivery_method :smtp, Mail::SMTP,
  16. address: "localhost",
  17. port: 25,
  18. domain: "localhost.localdomain",
  19. user_name: nil,
  20. password: nil,
  21. authentication: nil,
  22. enable_starttls_auto: true
  23. 1 add_delivery_method :file, Mail::FileDelivery,
  24. 1 location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
  25. 1 add_delivery_method :sendmail, Mail::Sendmail,
  26. location: "/usr/sbin/sendmail",
  27. arguments: "-i"
  28. 1 add_delivery_method :test, Mail::TestMailer
  29. end
  30. # Helpers for creating and wrapping delivery behavior, used by DeliveryMethods.
  31. 1 module ClassMethods
  32. # Provides a list of emails that have been delivered by Mail::TestMailer
  33. 1 delegate :deliveries, :deliveries=, to: Mail::TestMailer
  34. # Adds a new delivery method through the given class using the given
  35. # symbol as alias and the default options supplied.
  36. #
  37. # add_delivery_method :sendmail, Mail::Sendmail,
  38. # location: '/usr/sbin/sendmail',
  39. # arguments: '-i'
  40. 1 def add_delivery_method(symbol, klass, default_options = {})
  41. 4 class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
  42. 4 public_send(:"#{symbol}_settings=", default_options)
  43. 4 self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
  44. end
  45. 1 def wrap_delivery_behavior(mail, method = nil, options = nil) # :nodoc:
  46. method ||= delivery_method
  47. mail.delivery_handler = self
  48. case method
  49. when NilClass
  50. raise "Delivery method cannot be nil"
  51. when Symbol
  52. if klass = delivery_methods[method]
  53. mail.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {}))
  54. else
  55. raise "Invalid delivery method #{method.inspect}"
  56. end
  57. else
  58. mail.delivery_method(method)
  59. end
  60. mail.perform_deliveries = perform_deliveries
  61. mail.raise_delivery_errors = raise_delivery_errors
  62. end
  63. end
  64. 1 def wrap_delivery_behavior!(*args) # :nodoc:
  65. self.class.wrap_delivery_behavior(message, *args)
  66. end
  67. end
  68. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/inline_preview_interceptor.rb

54.17% lines covered

24 relevant lines. 13 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "base64"
  3. 1 module ActionMailer
  4. # Implements a mailer preview interceptor that converts image tag src attributes
  5. # that use inline cid: style URLs to data: style URLs so that they are visible
  6. # when previewing an HTML email in a web browser.
  7. #
  8. # This interceptor is enabled by default. To disable it, delete it from the
  9. # <tt>ActionMailer::Base.preview_interceptors</tt> array:
  10. #
  11. # ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
  12. #
  13. 1 class InlinePreviewInterceptor
  14. 1 PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
  15. 1 include Base64
  16. 1 def self.previewing_email(message) #:nodoc:
  17. new(message).transform!
  18. end
  19. 1 def initialize(message) #:nodoc:
  20. @message = message
  21. end
  22. 1 def transform! #:nodoc:
  23. return message if html_part.blank?
  24. html_part.body = html_part.decoded.gsub(PATTERN) do |match|
  25. if part = find_part(match[9..-2])
  26. %[src="#{data_url(part)}"]
  27. else
  28. match
  29. end
  30. end
  31. message
  32. end
  33. 1 private
  34. 1 attr_reader :message
  35. 1 def html_part
  36. @html_part ||= message.html_part
  37. end
  38. 1 def data_url(part)
  39. "data:#{part.mime_type};base64,#{strict_encode64(part.body.raw_source)}"
  40. end
  41. 1 def find_part(cid)
  42. message.all_parts.find { |p| p.attachment? && p.cid == cid }
  43. end
  44. end
  45. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/log_subscriber.rb

38.89% lines covered

18 relevant lines. 7 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/log_subscriber"
  3. 1 module ActionMailer
  4. # Implements the ActiveSupport::LogSubscriber for logging notifications when
  5. # email is delivered or received.
  6. 1 class LogSubscriber < ActiveSupport::LogSubscriber
  7. # An email was delivered.
  8. 1 def deliver(event)
  9. info do
  10. perform_deliveries = event.payload[:perform_deliveries]
  11. if perform_deliveries
  12. "Delivered mail #{event.payload[:message_id]} (#{event.duration.round(1)}ms)"
  13. else
  14. "Skipped delivery of mail #{event.payload[:message_id]} as `perform_deliveries` is false"
  15. end
  16. end
  17. debug { event.payload[:mail] }
  18. end
  19. # An email was generated.
  20. 1 def process(event)
  21. debug do
  22. mailer = event.payload[:mailer]
  23. action = event.payload[:action]
  24. "#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
  25. end
  26. end
  27. # Use the logger configured for ActionMailer::Base.
  28. 1 def logger
  29. ActionMailer::Base.logger
  30. end
  31. end
  32. end
  33. 1 ActionMailer::LogSubscriber.attach_to :action_mailer

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/mail_delivery_job.rb

47.37% lines covered

19 relevant lines. 9 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_job"
  3. 1 module ActionMailer
  4. # The <tt>ActionMailer::MailDeliveryJob</tt> class is used when you
  5. # want to send emails outside of the request-response cycle. It supports
  6. # sending either parameterized or normal mail.
  7. #
  8. # Exceptions are rescued and handled by the mailer class.
  9. 1 class MailDeliveryJob < ActiveJob::Base # :nodoc:
  10. 1 queue_as { ActionMailer::Base.deliver_later_queue_name }
  11. 1 rescue_from StandardError, with: :handle_exception_with_mailer_class
  12. 1 def perform(mailer, mail_method, delivery_method, args:, kwargs: nil, params: nil)
  13. mailer_class = params ? mailer.constantize.with(params) : mailer.constantize
  14. message = if kwargs
  15. mailer_class.public_send(mail_method, *args, **kwargs)
  16. else
  17. mailer_class.public_send(mail_method, *args)
  18. end
  19. message.send(delivery_method)
  20. end
  21. 1 private
  22. # "Deserialize" the mailer class name by hand in case another argument
  23. # (like a Global ID reference) raised DeserializationError.
  24. 1 def mailer_class
  25. if mailer = Array(@serialized_arguments).first || Array(arguments).first
  26. mailer.constantize
  27. end
  28. end
  29. 1 def handle_exception_with_mailer_class(exception)
  30. if klass = mailer_class
  31. klass.handle_exception exception
  32. else
  33. raise exception
  34. end
  35. end
  36. end
  37. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/mail_helper.rb

30.43% lines covered

23 relevant lines. 7 lines covered and 16 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionMailer
  3. # Provides helper methods for ActionMailer::Base that can be used for easily
  4. # formatting messages, accessing mailer or message instances, and the
  5. # attachments list.
  6. 1 module MailHelper
  7. # Take the text and format it, indented two spaces for each line, and
  8. # wrapped at 72 columns:
  9. #
  10. # text = <<-TEXT
  11. # This is
  12. # the paragraph.
  13. #
  14. # * item1 * item2
  15. # TEXT
  16. #
  17. # block_format text
  18. # # => " This is the paragraph.\n\n * item1\n * item2\n"
  19. 1 def block_format(text)
  20. formatted = text.split(/\n\r?\n/).collect { |paragraph|
  21. format_paragraph(paragraph)
  22. }.join("\n\n")
  23. # Make list points stand on their own line
  24. formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { " #{$1} #{$2.strip}\n" }
  25. formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { " #{$1} #{$2.strip}\n" }
  26. formatted
  27. end
  28. # Access the mailer instance.
  29. 1 def mailer
  30. @_controller
  31. end
  32. # Access the message instance.
  33. 1 def message
  34. @_message
  35. end
  36. # Access the message attachments list.
  37. 1 def attachments
  38. mailer.attachments
  39. end
  40. # Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
  41. # By default column length +len+ equals 72 characters and indent
  42. # +indent+ equal two spaces.
  43. #
  44. # my_text = 'Here is a sample text with more than 40 characters'
  45. #
  46. # format_paragraph(my_text, 25, 4)
  47. # # => " Here is a sample text with\n more than 40 characters"
  48. 1 def format_paragraph(text, len = 72, indent = 2)
  49. sentences = [[]]
  50. text.split.each do |word|
  51. if sentences.first.present? && (sentences.last + [word]).join(" ").length > len
  52. sentences << [word]
  53. else
  54. sentences.last << word
  55. end
  56. end
  57. indentation = " " * indent
  58. sentences.map! { |sentence|
  59. "#{indentation}#{sentence.join(' ')}"
  60. }.join "\n"
  61. end
  62. end
  63. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/message_delivery.rb

37.78% lines covered

45 relevant lines. 17 lines covered and 28 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "delegate"
  3. 1 module ActionMailer
  4. # The <tt>ActionMailer::MessageDelivery</tt> class is used by
  5. # ActionMailer::Base when creating a new mailer.
  6. # <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy
  7. # created <tt>Mail::Message</tt>. You can get direct access to the
  8. # <tt>Mail::Message</tt>, deliver the email or schedule the email to be sent
  9. # through Active Job.
  10. #
  11. # Notifier.welcome(User.first) # an ActionMailer::MessageDelivery object
  12. # Notifier.welcome(User.first).deliver_now # sends the email
  13. # Notifier.welcome(User.first).deliver_later # enqueue email delivery as a job through Active Job
  14. # Notifier.welcome(User.first).message # a Mail::Message object
  15. 1 class MessageDelivery < Delegator
  16. 1 def initialize(mailer_class, action, *args) #:nodoc:
  17. @mailer_class, @action, @args = mailer_class, action, args
  18. # The mail is only processed if we try to call any methods on it.
  19. # Typical usage will leave it unloaded and call deliver_later.
  20. @processed_mailer = nil
  21. @mail_message = nil
  22. end
  23. 1 ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
  24. # Method calls are delegated to the Mail::Message that's ready to deliver.
  25. 1 def __getobj__ #:nodoc:
  26. @mail_message ||= processed_mailer.message
  27. end
  28. # Unused except for delegator internals (dup, marshalling).
  29. 1 def __setobj__(mail_message) #:nodoc:
  30. @mail_message = mail_message
  31. end
  32. # Returns the resulting Mail::Message
  33. 1 def message
  34. __getobj__
  35. end
  36. # Was the delegate loaded, causing the mailer action to be processed?
  37. 1 def processed?
  38. @processed_mailer || @mail_message
  39. end
  40. # Enqueues the email to be delivered through Active Job. When the
  41. # job runs it will send the email using +deliver_now!+. That means
  42. # that the message will be sent bypassing checking +perform_deliveries+
  43. # and +raise_delivery_errors+, so use with caution.
  44. #
  45. # Notifier.welcome(User.first).deliver_later!
  46. # Notifier.welcome(User.first).deliver_later!(wait: 1.hour)
  47. # Notifier.welcome(User.first).deliver_later!(wait_until: 10.hours.from_now)
  48. # Notifier.welcome(User.first).deliver_later!(priority: 10)
  49. #
  50. # Options:
  51. #
  52. # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay
  53. # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time
  54. # * <tt>:queue</tt> - Enqueue the email on the specified queue
  55. # * <tt>:priority</tt> - Enqueues the email with the specified priority
  56. #
  57. # By default, the email will be enqueued using <tt>ActionMailer::DeliveryJob</tt>. Each
  58. # <tt>ActionMailer::Base</tt> class can specify the job to use by setting the class variable
  59. # +delivery_job+.
  60. #
  61. # class AccountRegistrationMailer < ApplicationMailer
  62. # self.delivery_job = RegistrationDeliveryJob
  63. # end
  64. 1 def deliver_later!(options = {})
  65. enqueue_delivery :deliver_now!, options
  66. end
  67. # Enqueues the email to be delivered through Active Job. When the
  68. # job runs it will send the email using +deliver_now+.
  69. #
  70. # Notifier.welcome(User.first).deliver_later
  71. # Notifier.welcome(User.first).deliver_later(wait: 1.hour)
  72. # Notifier.welcome(User.first).deliver_later(wait_until: 10.hours.from_now)
  73. # Notifier.welcome(User.first).deliver_later(priority: 10)
  74. #
  75. # Options:
  76. #
  77. # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay.
  78. # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time.
  79. # * <tt>:queue</tt> - Enqueue the email on the specified queue.
  80. # * <tt>:priority</tt> - Enqueues the email with the specified priority
  81. #
  82. # By default, the email will be enqueued using <tt>ActionMailer::DeliveryJob</tt>. Each
  83. # <tt>ActionMailer::Base</tt> class can specify the job to use by setting the class variable
  84. # +delivery_job+.
  85. #
  86. # class AccountRegistrationMailer < ApplicationMailer
  87. # self.delivery_job = RegistrationDeliveryJob
  88. # end
  89. 1 def deliver_later(options = {})
  90. enqueue_delivery :deliver_now, options
  91. end
  92. # Delivers an email without checking +perform_deliveries+ and +raise_delivery_errors+,
  93. # so use with caution.
  94. #
  95. # Notifier.welcome(User.first).deliver_now!
  96. #
  97. 1 def deliver_now!
  98. processed_mailer.handle_exceptions do
  99. message.deliver!
  100. end
  101. end
  102. # Delivers an email:
  103. #
  104. # Notifier.welcome(User.first).deliver_now
  105. #
  106. 1 def deliver_now
  107. processed_mailer.handle_exceptions do
  108. message.deliver
  109. end
  110. end
  111. 1 private
  112. # Returns the processed Mailer instance. We keep this instance
  113. # on hand so we can delegate exception handling to it.
  114. 1 def processed_mailer
  115. @processed_mailer ||= @mailer_class.new.tap do |mailer|
  116. mailer.process @action, *@args
  117. end
  118. end
  119. 1 def enqueue_delivery(delivery_method, options = {})
  120. if processed?
  121. ::Kernel.raise "You've accessed the message before asking to " \
  122. "deliver it later, so you may have made local changes that would " \
  123. "be silently lost if we enqueued a job to deliver it. Why? Only " \
  124. "the mailer method *arguments* are passed with the delivery job! " \
  125. "Do not access the message in any way if you mean to deliver it " \
  126. "later. Workarounds: 1. don't touch the message before calling " \
  127. "#deliver_later, 2. only touch the message *within your mailer " \
  128. "method*, or 3. use a custom Active Job instead of #deliver_later."
  129. else
  130. job = @mailer_class.delivery_job
  131. if use_new_args?(job)
  132. job.set(options).perform_later(
  133. @mailer_class.name, @action.to_s, delivery_method.to_s, args: @args)
  134. elsif job <= DeliveryJob
  135. job.set(options).perform_later(
  136. @mailer_class.name, @action.to_s, delivery_method.to_s, *@args)
  137. else
  138. ActiveSupport::Deprecation.warn(<<~EOM)
  139. In Rails 6.2, Action Mailer will pass the mail arguments inside the `:args` keyword argument.
  140. The `perform` method of the #{job} needs to change and forward the mail arguments
  141. from the `args` keyword argument.
  142. The `perform` method should now look like:
  143. `def perform(mailer, mail_method, delivery, args:)`
  144. EOM
  145. job.set(options).perform_later(
  146. @mailer_class.name, @action.to_s, delivery_method.to_s, *@args)
  147. end
  148. end
  149. end
  150. 1 def use_new_args?(job)
  151. parameters = job.public_instance_method(:perform).parameters
  152. parameters.find do |key, name|
  153. return true if key == :keyreq && name == :args
  154. key == :keyrest
  155. end
  156. end
  157. end
  158. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/parameterized.rb

52.27% lines covered

44 relevant lines. 23 lines covered and 21 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionMailer
  3. # Provides the option to parameterize mailers in order to share instance variable
  4. # setup, processing, and common headers.
  5. #
  6. # Consider this example that does not use parameterization:
  7. #
  8. # class InvitationsMailer < ApplicationMailer
  9. # def account_invitation(inviter, invitee)
  10. # @account = inviter.account
  11. # @inviter = inviter
  12. # @invitee = invitee
  13. #
  14. # subject = "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
  15. #
  16. # mail \
  17. # subject: subject,
  18. # to: invitee.email_address,
  19. # from: common_address(inviter),
  20. # reply_to: inviter.email_address_with_name
  21. # end
  22. #
  23. # def project_invitation(project, inviter, invitee)
  24. # @account = inviter.account
  25. # @project = project
  26. # @inviter = inviter
  27. # @invitee = invitee
  28. # @summarizer = ProjectInvitationSummarizer.new(@project.bucket)
  29. #
  30. # subject = "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
  31. #
  32. # mail \
  33. # subject: subject,
  34. # to: invitee.email_address,
  35. # from: common_address(inviter),
  36. # reply_to: inviter.email_address_with_name
  37. # end
  38. #
  39. # def bulk_project_invitation(projects, inviter, invitee)
  40. # @account = inviter.account
  41. # @projects = projects.sort_by(&:name)
  42. # @inviter = inviter
  43. # @invitee = invitee
  44. #
  45. # subject = "#{@inviter.name.familiar} added you to some new stuff in Basecamp (#{@account.name})"
  46. #
  47. # mail \
  48. # subject: subject,
  49. # to: invitee.email_address,
  50. # from: common_address(inviter),
  51. # reply_to: inviter.email_address_with_name
  52. # end
  53. # end
  54. #
  55. # InvitationsMailer.account_invitation(person_a, person_b).deliver_later
  56. #
  57. # Using parameterized mailers, this can be rewritten as:
  58. #
  59. # class InvitationsMailer < ApplicationMailer
  60. # before_action { @inviter, @invitee = params[:inviter], params[:invitee] }
  61. # before_action { @account = params[:inviter].account }
  62. #
  63. # default to: -> { @invitee.email_address },
  64. # from: -> { common_address(@inviter) },
  65. # reply_to: -> { @inviter.email_address_with_name }
  66. #
  67. # def account_invitation
  68. # mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
  69. # end
  70. #
  71. # def project_invitation
  72. # @project = params[:project]
  73. # @summarizer = ProjectInvitationSummarizer.new(@project.bucket)
  74. #
  75. # mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
  76. # end
  77. #
  78. # def bulk_project_invitation
  79. # @projects = params[:projects].sort_by(&:name)
  80. #
  81. # mail subject: "#{@inviter.name.familiar} added you to some new stuff in Basecamp (#{@account.name})"
  82. # end
  83. # end
  84. #
  85. # InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later
  86. 1 module Parameterized
  87. 1 extend ActiveSupport::Concern
  88. 1 included do
  89. 1 attr_accessor :params
  90. end
  91. 1 module ClassMethods
  92. # Provide the parameters to the mailer in order to use them in the instance methods and callbacks.
  93. #
  94. # InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later
  95. #
  96. # See Parameterized documentation for full example.
  97. 1 def with(params)
  98. ActionMailer::Parameterized::Mailer.new(self, params)
  99. end
  100. end
  101. 1 class Mailer # :nodoc:
  102. 1 def initialize(mailer, params)
  103. @mailer, @params = mailer, params
  104. end
  105. 1 private
  106. 1 def method_missing(method_name, *args)
  107. if @mailer.action_methods.include?(method_name.to_s)
  108. ActionMailer::Parameterized::MessageDelivery.new(@mailer, method_name, @params, *args)
  109. else
  110. super
  111. end
  112. end
  113. 1 ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
  114. 1 def respond_to_missing?(method, include_all = false)
  115. @mailer.respond_to?(method, include_all)
  116. end
  117. end
  118. 1 class DeliveryJob < ActionMailer::DeliveryJob # :nodoc:
  119. 1 def perform(mailer, mail_method, delivery_method, params, *args)
  120. mailer.constantize.with(params).public_send(mail_method, *args).send(delivery_method)
  121. end
  122. 1 ruby2_keywords(:perform) if respond_to?(:ruby2_keywords, true)
  123. end
  124. 1 class MessageDelivery < ActionMailer::MessageDelivery # :nodoc:
  125. 1 def initialize(mailer_class, action, params, *args)
  126. super(mailer_class, action, *args)
  127. @params = params
  128. end
  129. 1 ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
  130. 1 private
  131. 1 def processed_mailer
  132. @processed_mailer ||= @mailer_class.new.tap do |mailer|
  133. mailer.params = @params
  134. mailer.process @action, *@args
  135. end
  136. end
  137. 1 def enqueue_delivery(delivery_method, options = {})
  138. if processed?
  139. super
  140. else
  141. job = delivery_job_class
  142. if job <= MailDeliveryJob
  143. job.set(options).perform_later(
  144. @mailer_class.name, @action.to_s, delivery_method.to_s, params: @params, args: @args)
  145. else
  146. job.set(options).perform_later(
  147. @mailer_class.name, @action.to_s, delivery_method.to_s, @params, *@args)
  148. end
  149. end
  150. end
  151. 1 def delivery_job_class
  152. if @mailer_class.delivery_job <= MailDeliveryJob
  153. @mailer_class.delivery_job
  154. else
  155. Parameterized::DeliveryJob
  156. end
  157. end
  158. end
  159. end
  160. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/preview.rb

55.93% lines covered

59 relevant lines. 33 lines covered and 26 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/descendants_tracker"
  3. 1 module ActionMailer
  4. 1 module Previews #:nodoc:
  5. 1 extend ActiveSupport::Concern
  6. 1 included do
  7. # Set the location of mailer previews through app configuration:
  8. #
  9. # config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
  10. #
  11. 1 mattr_accessor :preview_path, instance_writer: false
  12. # Enable or disable mailer previews through app configuration:
  13. #
  14. # config.action_mailer.show_previews = true
  15. #
  16. # Defaults to +true+ for development environment
  17. #
  18. 1 mattr_accessor :show_previews, instance_writer: false
  19. # :nodoc:
  20. 1 mattr_accessor :preview_interceptors, instance_writer: false, default: [ActionMailer::InlinePreviewInterceptor]
  21. end
  22. 1 module ClassMethods
  23. # Register one or more Interceptors which will be called before mail is previewed.
  24. 1 def register_preview_interceptors(*interceptors)
  25. 1 interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
  26. end
  27. # Unregister one or more previously registered Interceptors.
  28. 1 def unregister_preview_interceptors(*interceptors)
  29. interceptors.flatten.compact.each { |interceptor| unregister_preview_interceptor(interceptor) }
  30. end
  31. # Register an Interceptor which will be called before mail is previewed.
  32. # Either a class or a string can be passed in as the Interceptor. If a
  33. # string is passed in it will be constantized.
  34. 1 def register_preview_interceptor(interceptor)
  35. preview_interceptor = interceptor_class_for(interceptor)
  36. unless preview_interceptors.include?(preview_interceptor)
  37. preview_interceptors << preview_interceptor
  38. end
  39. end
  40. # Unregister a previously registered Interceptor.
  41. # Either a class or a string can be passed in as the Interceptor. If a
  42. # string is passed in it will be constantized.
  43. 1 def unregister_preview_interceptor(interceptor)
  44. preview_interceptors.delete(interceptor_class_for(interceptor))
  45. end
  46. 1 private
  47. 1 def interceptor_class_for(interceptor)
  48. case interceptor
  49. when String, Symbol
  50. interceptor.to_s.camelize.constantize
  51. else
  52. interceptor
  53. end
  54. end
  55. end
  56. end
  57. 1 class Preview
  58. 1 extend ActiveSupport::DescendantsTracker
  59. 1 attr_reader :params
  60. 1 def initialize(params = {})
  61. @params = params
  62. end
  63. 1 class << self
  64. # Returns all mailer preview classes.
  65. 1 def all
  66. load_previews if descendants.empty?
  67. descendants
  68. end
  69. # Returns the mail object for the given email name. The registered preview
  70. # interceptors will be informed so that they can transform the message
  71. # as they would if the mail was actually being delivered.
  72. 1 def call(email, params = {})
  73. preview = new(params)
  74. message = preview.public_send(email)
  75. inform_preview_interceptors(message)
  76. message
  77. end
  78. # Returns all of the available email previews.
  79. 1 def emails
  80. public_instance_methods(false).map(&:to_s).sort
  81. end
  82. # Returns +true+ if the email exists.
  83. 1 def email_exists?(email)
  84. emails.include?(email)
  85. end
  86. # Returns +true+ if the preview exists.
  87. 1 def exists?(preview)
  88. all.any? { |p| p.preview_name == preview }
  89. end
  90. # Find a mailer preview by its underscored class name.
  91. 1 def find(preview)
  92. all.find { |p| p.preview_name == preview }
  93. end
  94. # Returns the underscored name of the mailer preview without the suffix.
  95. 1 def preview_name
  96. name.delete_suffix("Preview").underscore
  97. end
  98. 1 private
  99. 1 def load_previews
  100. if preview_path
  101. Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
  102. end
  103. end
  104. 1 def preview_path
  105. Base.preview_path
  106. end
  107. 1 def show_previews
  108. Base.show_previews
  109. end
  110. 1 def inform_preview_interceptors(message)
  111. Base.preview_interceptors.each do |interceptor|
  112. interceptor.previewing_email(message)
  113. end
  114. end
  115. end
  116. end
  117. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/rescuable.rb

64.29% lines covered

14 relevant lines. 9 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionMailer #:nodoc:
  3. # Provides +rescue_from+ for mailers. Wraps mailer action processing,
  4. # mail job processing, and mail delivery.
  5. 1 module Rescuable
  6. 1 extend ActiveSupport::Concern
  7. 1 include ActiveSupport::Rescuable
  8. 1 class_methods do
  9. 1 def handle_exception(exception) #:nodoc:
  10. rescue_with_handler(exception) || raise(exception)
  11. end
  12. end
  13. 1 def handle_exceptions #:nodoc:
  14. yield
  15. rescue => exception
  16. rescue_with_handler(exception) || raise
  17. end
  18. 1 private
  19. 1 def process(*)
  20. handle_exceptions do
  21. super
  22. end
  23. end
  24. end
  25. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/test_case.rb

59.42% lines covered

69 relevant lines. 41 lines covered and 28 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/test_case"
  3. 1 require "rails-dom-testing"
  4. 1 module ActionMailer
  5. 1 class NonInferrableMailerError < ::StandardError
  6. 1 def initialize(name)
  7. super "Unable to determine the mailer to test from #{name}. " \
  8. "You'll need to specify it using tests YourMailer in your " \
  9. "test case definition"
  10. end
  11. end
  12. 1 class TestCase < ActiveSupport::TestCase
  13. 1 module ClearTestDeliveries
  14. 1 extend ActiveSupport::Concern
  15. 1 included do
  16. 5 setup :clear_test_deliveries
  17. 5 teardown :clear_test_deliveries
  18. end
  19. 1 private
  20. 1 def clear_test_deliveries
  21. 6 if ActionMailer::Base.delivery_method == :test
  22. 6 ActionMailer::Base.deliveries.clear
  23. end
  24. end
  25. end
  26. 1 module Behavior
  27. 1 extend ActiveSupport::Concern
  28. 1 include ActiveSupport::Testing::ConstantLookup
  29. 1 include TestHelper
  30. 1 include Rails::Dom::Testing::Assertions::SelectorAssertions
  31. 1 include Rails::Dom::Testing::Assertions::DomAssertions
  32. 1 included do
  33. 1 class_attribute :_mailer_class
  34. 1 setup :initialize_test_deliveries
  35. 1 setup :set_expected_mail
  36. 1 teardown :restore_test_deliveries
  37. 1 ActiveSupport.run_load_hooks(:action_mailer_test_case, self)
  38. end
  39. 1 module ClassMethods
  40. 1 def tests(mailer)
  41. case mailer
  42. when String, Symbol
  43. self._mailer_class = mailer.to_s.camelize.constantize
  44. when Module
  45. self._mailer_class = mailer
  46. else
  47. raise NonInferrableMailerError.new(mailer)
  48. end
  49. end
  50. 1 def mailer_class
  51. if mailer = _mailer_class
  52. mailer
  53. else
  54. tests determine_default_mailer(name)
  55. end
  56. end
  57. 1 def determine_default_mailer(name)
  58. mailer = determine_constant_from_test_name(name) do |constant|
  59. Class === constant && constant < ActionMailer::Base
  60. end
  61. raise NonInferrableMailerError.new(name) if mailer.nil?
  62. mailer
  63. end
  64. end
  65. 1 private
  66. 1 def initialize_test_deliveries
  67. set_delivery_method :test
  68. @old_perform_deliveries = ActionMailer::Base.perform_deliveries
  69. ActionMailer::Base.perform_deliveries = true
  70. ActionMailer::Base.deliveries.clear
  71. end
  72. 1 def restore_test_deliveries
  73. restore_delivery_method
  74. ActionMailer::Base.perform_deliveries = @old_perform_deliveries
  75. end
  76. 1 def set_delivery_method(method)
  77. @old_delivery_method = ActionMailer::Base.delivery_method
  78. ActionMailer::Base.delivery_method = method
  79. end
  80. 1 def restore_delivery_method
  81. ActionMailer::Base.deliveries.clear
  82. ActionMailer::Base.delivery_method = @old_delivery_method
  83. end
  84. 1 def set_expected_mail
  85. @expected = Mail.new
  86. @expected.content_type ["text", "plain", { "charset" => charset }]
  87. @expected.mime_version = "1.0"
  88. end
  89. 1 def charset
  90. "UTF-8"
  91. end
  92. 1 def encode(subject)
  93. Mail::Encodings.q_value_encode(subject, charset)
  94. end
  95. 1 def read_fixture(action)
  96. IO.readlines(File.join(Rails.root, "test", "fixtures", self.class.mailer_class.name.underscore, action))
  97. end
  98. end
  99. 1 include Behavior
  100. end
  101. end

vender/bundle/ruby/2.6.0/gems/actionmailer-6.1.3/lib/action_mailer/test_helper.rb

42.31% lines covered

26 relevant lines. 11 lines covered and 15 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_job"
  3. 1 module ActionMailer
  4. # Provides helper methods for testing Action Mailer, including #assert_emails
  5. # and #assert_no_emails.
  6. 1 module TestHelper
  7. 1 include ActiveJob::TestHelper
  8. # Asserts that the number of emails sent matches the given number.
  9. #
  10. # def test_emails
  11. # assert_emails 0
  12. # ContactMailer.welcome.deliver_now
  13. # assert_emails 1
  14. # ContactMailer.welcome.deliver_now
  15. # assert_emails 2
  16. # end
  17. #
  18. # If a block is passed, that block should cause the specified number of
  19. # emails to be sent.
  20. #
  21. # def test_emails_again
  22. # assert_emails 1 do
  23. # ContactMailer.welcome.deliver_now
  24. # end
  25. #
  26. # assert_emails 2 do
  27. # ContactMailer.welcome.deliver_now
  28. # ContactMailer.welcome.deliver_later
  29. # end
  30. # end
  31. 1 def assert_emails(number, &block)
  32. if block_given?
  33. original_count = ActionMailer::Base.deliveries.size
  34. perform_enqueued_jobs(only: ->(job) { delivery_job_filter(job) }, &block)
  35. new_count = ActionMailer::Base.deliveries.size
  36. assert_equal number, new_count - original_count, "#{number} emails expected, but #{new_count - original_count} were sent"
  37. else
  38. assert_equal number, ActionMailer::Base.deliveries.size
  39. end
  40. end
  41. # Asserts that no emails have been sent.
  42. #
  43. # def test_emails
  44. # assert_no_emails
  45. # ContactMailer.welcome.deliver_now
  46. # assert_emails 1
  47. # end
  48. #
  49. # If a block is passed, that block should not cause any emails to be sent.
  50. #
  51. # def test_emails_again
  52. # assert_no_emails do
  53. # # No emails should be sent from this block
  54. # end
  55. # end
  56. #
  57. # Note: This assertion is simply a shortcut for:
  58. #
  59. # assert_emails 0, &block
  60. 1 def assert_no_emails(&block)
  61. assert_emails 0, &block
  62. end
  63. # Asserts that the number of emails enqueued for later delivery matches
  64. # the given number.
  65. #
  66. # def test_emails
  67. # assert_enqueued_emails 0
  68. # ContactMailer.welcome.deliver_later
  69. # assert_enqueued_emails 1
  70. # ContactMailer.welcome.deliver_later
  71. # assert_enqueued_emails 2
  72. # end
  73. #
  74. # If a block is passed, that block should cause the specified number of
  75. # emails to be enqueued.
  76. #
  77. # def test_emails_again
  78. # assert_enqueued_emails 1 do
  79. # ContactMailer.welcome.deliver_later
  80. # end
  81. #
  82. # assert_enqueued_emails 2 do
  83. # ContactMailer.welcome.deliver_later
  84. # ContactMailer.welcome.deliver_later
  85. # end
  86. # end
  87. 1 def assert_enqueued_emails(number, &block)
  88. assert_enqueued_jobs(number, only: ->(job) { delivery_job_filter(job) }, &block)
  89. end
  90. # Asserts that a specific email has been enqueued, optionally
  91. # matching arguments.
  92. #
  93. # def test_email
  94. # ContactMailer.welcome.deliver_later
  95. # assert_enqueued_email_with ContactMailer, :welcome
  96. # end
  97. #
  98. # def test_email_with_arguments
  99. # ContactMailer.welcome("Hello", "Goodbye").deliver_later
  100. # assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
  101. # end
  102. #
  103. # If a block is passed, that block should cause the specified email
  104. # to be enqueued.
  105. #
  106. # def test_email_in_block
  107. # assert_enqueued_email_with ContactMailer, :welcome do
  108. # ContactMailer.welcome.deliver_later
  109. # end
  110. # end
  111. #
  112. # If +args+ is provided as a Hash, a parameterized email is matched.
  113. #
  114. # def test_parameterized_email
  115. # assert_enqueued_email_with ContactMailer, :welcome,
  116. # args: {email: 'user@example.com'} do
  117. # ContactMailer.with(email: 'user@example.com').welcome.deliver_later
  118. # end
  119. # end
  120. 1 def assert_enqueued_email_with(mailer, method, args: nil, queue: ActionMailer::Base.deliver_later_queue_name || "default", &block)
  121. args = if args.is_a?(Hash)
  122. [mailer.to_s, method.to_s, "deliver_now", params: args, args: []]
  123. else
  124. [mailer.to_s, method.to_s, "deliver_now", args: Array(args)]
  125. end
  126. assert_enqueued_with(job: mailer.delivery_job, args: args, queue: queue.to_s, &block)
  127. end
  128. # Asserts that no emails are enqueued for later delivery.
  129. #
  130. # def test_no_emails
  131. # assert_no_enqueued_emails
  132. # ContactMailer.welcome.deliver_later
  133. # assert_enqueued_emails 1
  134. # end
  135. #
  136. # If a block is provided, it should not cause any emails to be enqueued.
  137. #
  138. # def test_no_emails
  139. # assert_no_enqueued_emails do
  140. # # No emails should be enqueued from this block
  141. # end
  142. # end
  143. 1 def assert_no_enqueued_emails(&block)
  144. assert_enqueued_emails 0, &block
  145. end
  146. 1 private
  147. 1 def delivery_job_filter(job)
  148. job_class = job.is_a?(Hash) ? job.fetch(:job) : job.class
  149. Base.descendants.map(&:delivery_job).include?(job_class) ||
  150. ActionMailer::Parameterized::DeliveryJob == job_class
  151. end
  152. end
  153. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/asset_paths.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module AbstractController
  3. 1 module AssetPaths #:nodoc:
  4. 1 extend ActiveSupport::Concern
  5. 1 included do
  6. 2 config_accessor :asset_host, :assets_dir, :javascripts_dir,
  7. :stylesheets_dir, :default_asset_host_protocol, :relative_url_root
  8. end
  9. end
  10. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/base.rb

77.78% lines covered

90 relevant lines. 70 lines covered and 20 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "abstract_controller/error"
  3. 1 require "active_support/configurable"
  4. 1 require "active_support/descendants_tracker"
  5. 1 require "active_support/core_ext/module/anonymous"
  6. 1 require "active_support/core_ext/module/attr_internal"
  7. 1 module AbstractController
  8. # Raised when a non-existing controller action is triggered.
  9. 1 class ActionNotFound < StandardError
  10. 1 attr_reader :controller, :action
  11. 1 def initialize(message = nil, controller = nil, action = nil)
  12. @controller = controller
  13. @action = action
  14. super(message)
  15. end
  16. 1 class Correction
  17. 1 def initialize(error)
  18. @error = error
  19. end
  20. 1 def corrections
  21. if @error.action
  22. maybe_these = @error.controller.class.action_methods
  23. maybe_these.sort_by { |n|
  24. DidYouMean::Jaro.distance(@error.action.to_s, n)
  25. }.reverse.first(4)
  26. else
  27. []
  28. end
  29. end
  30. end
  31. # We may not have DYM, and DYM might not let us register error handlers
  32. 1 if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
  33. DidYouMean.correct_error(self, Correction)
  34. end
  35. end
  36. # AbstractController::Base is a low-level API. Nobody should be
  37. # using it directly, and subclasses (like ActionController::Base) are
  38. # expected to provide their own +render+ method, since rendering means
  39. # different things depending on the context.
  40. 1 class Base
  41. ##
  42. # Returns the body of the HTTP response sent by the controller.
  43. 1 attr_internal :response_body
  44. ##
  45. # Returns the name of the action this controller is processing.
  46. 1 attr_internal :action_name
  47. ##
  48. # Returns the formats that can be processed by the controller.
  49. 1 attr_internal :formats
  50. 1 include ActiveSupport::Configurable
  51. 1 extend ActiveSupport::DescendantsTracker
  52. 1 class << self
  53. 1 attr_reader :abstract
  54. 1 alias_method :abstract?, :abstract
  55. # Define a controller as abstract. See internal_methods for more
  56. # details.
  57. 1 def abstract!
  58. 5 @abstract = true
  59. end
  60. 1 def inherited(klass) # :nodoc:
  61. # Define the abstract ivar on subclasses so that we don't get
  62. # uninitialized ivar warnings
  63. 7 unless klass.instance_variable_defined?(:@abstract)
  64. 7 klass.instance_variable_set(:@abstract, false)
  65. end
  66. 7 super
  67. end
  68. # A list of all internal methods for a controller. This finds the first
  69. # abstract superclass of a controller, and gets a list of all public
  70. # instance methods on that abstract class. Public instance methods of
  71. # a controller would normally be considered action methods, so methods
  72. # declared on abstract classes are being removed.
  73. # (<tt>ActionController::Metal</tt> and ActionController::Base are defined as abstract)
  74. 1 def internal_methods
  75. 2 controller = self
  76. 2 controller = controller.superclass until controller.abstract?
  77. 2 controller.public_instance_methods(true)
  78. end
  79. # A list of method names that should be considered actions. This
  80. # includes all public instance methods on a controller, less
  81. # any internal methods (see internal_methods), adding back in
  82. # any methods that are internal, but still exist on the class
  83. # itself.
  84. #
  85. # ==== Returns
  86. # * <tt>Set</tt> - A set of all methods that should be considered actions.
  87. 1 def action_methods
  88. 2 @action_methods ||= begin
  89. # All public instance methods of this class, including ancestors
  90. 2 methods = (public_instance_methods(true) -
  91. # Except for public instance methods of Base and its ancestors
  92. internal_methods +
  93. # Be sure to include shadowed public instance methods of this class
  94. public_instance_methods(false))
  95. 2 methods.map!(&:to_s)
  96. 2 methods.to_set
  97. end
  98. end
  99. # action_methods are cached and there is sometimes a need to refresh
  100. # them. ::clear_action_methods! allows you to do that, so next time
  101. # you run action_methods, they will be recalculated.
  102. 1 def clear_action_methods!
  103. 331 @action_methods = nil
  104. end
  105. # Returns the full controller name, underscored, without the ending Controller.
  106. #
  107. # class MyApp::MyPostsController < AbstractController::Base
  108. #
  109. # end
  110. #
  111. # MyApp::MyPostsController.controller_path # => "my_app/my_posts"
  112. #
  113. # ==== Returns
  114. # * <tt>String</tt>
  115. 1 def controller_path
  116. 8 @controller_path ||= name.delete_suffix("Controller").underscore unless anonymous?
  117. end
  118. # Refresh the cached action_methods when a new action_method is added.
  119. 1 def method_added(name)
  120. 331 super
  121. 331 clear_action_methods!
  122. end
  123. end
  124. 1 abstract!
  125. # Calls the action going through the entire action dispatch stack.
  126. #
  127. # The actual method that is called is determined by calling
  128. # #method_for_action. If no method can handle the action, then an
  129. # AbstractController::ActionNotFound error is raised.
  130. #
  131. # ==== Returns
  132. # * <tt>self</tt>
  133. 1 def process(action, *args)
  134. 1 @_action_name = action.to_s
  135. 1 unless action_name = _find_action_name(@_action_name)
  136. raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action)
  137. end
  138. 1 @_response_body = nil
  139. 1 process_action(action_name, *args)
  140. end
  141. # Delegates to the class' ::controller_path
  142. 1 def controller_path
  143. self.class.controller_path
  144. end
  145. # Delegates to the class' ::action_methods
  146. 1 def action_methods
  147. self.class.action_methods
  148. end
  149. # Returns true if a method for the action is available and
  150. # can be dispatched, false otherwise.
  151. #
  152. # Notice that <tt>action_methods.include?("foo")</tt> may return
  153. # false and <tt>available_action?("foo")</tt> returns true because
  154. # this method considers actions that are also available
  155. # through other means, for example, implicit render ones.
  156. #
  157. # ==== Parameters
  158. # * <tt>action_name</tt> - The name of an action to be tested
  159. 1 def available_action?(action_name)
  160. _find_action_name(action_name)
  161. end
  162. # Tests if a response body is set. Used to determine if the
  163. # +process_action+ callback needs to be terminated in
  164. # +AbstractController::Callbacks+.
  165. 1 def performed?
  166. response_body
  167. end
  168. # Returns true if the given controller is capable of rendering
  169. # a path. A subclass of +AbstractController::Base+
  170. # may return false. An Email controller for example does not
  171. # support paths, only full URLs.
  172. 1 def self.supports_path?
  173. true
  174. end
  175. 1 def inspect # :nodoc:
  176. "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
  177. end
  178. 1 private
  179. # Returns true if the name can be considered an action because
  180. # it has a method defined in the controller.
  181. #
  182. # ==== Parameters
  183. # * <tt>name</tt> - The name of an action to be tested
  184. 1 def action_method?(name)
  185. 1 self.class.action_methods.include?(name)
  186. end
  187. # Call the action. Override this in a subclass to modify the
  188. # behavior around processing an action. This, and not #process,
  189. # is the intended way to override action dispatching.
  190. #
  191. # Notice that the first argument is the method to be dispatched
  192. # which is *not* necessarily the same as the action name.
  193. 1 def process_action(method_name, *args)
  194. 1 send_action(method_name, *args)
  195. end
  196. # Actually call the method associated with the action. Override
  197. # this method if you wish to change how action methods are called,
  198. # not to add additional behavior around it. For example, you would
  199. # override #send_action if you want to inject arguments into the
  200. # method.
  201. 1 alias send_action send
  202. # If the action name was not found, but a method called "action_missing"
  203. # was found, #method_for_action will return "_handle_action_missing".
  204. # This method calls #action_missing with the current action name.
  205. 1 def _handle_action_missing(*args)
  206. action_missing(@_action_name, *args)
  207. end
  208. # Takes an action name and returns the name of the method that will
  209. # handle the action.
  210. #
  211. # It checks if the action name is valid and returns false otherwise.
  212. #
  213. # See method_for_action for more information.
  214. #
  215. # ==== Parameters
  216. # * <tt>action_name</tt> - An action name to find a method name for
  217. #
  218. # ==== Returns
  219. # * <tt>string</tt> - The name of the method that handles the action
  220. # * false - No valid method name could be found.
  221. # Raise +AbstractController::ActionNotFound+.
  222. 1 def _find_action_name(action_name)
  223. 1 _valid_action_name?(action_name) && method_for_action(action_name)
  224. end
  225. # Takes an action name and returns the name of the method that will
  226. # handle the action. In normal cases, this method returns the same
  227. # name as it receives. By default, if #method_for_action receives
  228. # a name that is not an action, it will look for an #action_missing
  229. # method and return "_handle_action_missing" if one is found.
  230. #
  231. # Subclasses may override this method to add additional conditions
  232. # that should be considered an action. For instance, an HTTP controller
  233. # with a template matching the action name is considered to exist.
  234. #
  235. # If you override this method to handle additional cases, you may
  236. # also provide a method (like +_handle_method_missing+) to handle
  237. # the case.
  238. #
  239. # If none of these conditions are true, and +method_for_action+
  240. # returns +nil+, an +AbstractController::ActionNotFound+ exception will be raised.
  241. #
  242. # ==== Parameters
  243. # * <tt>action_name</tt> - An action name to find a method name for
  244. #
  245. # ==== Returns
  246. # * <tt>string</tt> - The name of the method that handles the action
  247. # * <tt>nil</tt> - No method name could be found.
  248. 1 def method_for_action(action_name)
  249. 1 if action_method?(action_name)
  250. 1 action_name
  251. elsif respond_to?(:action_missing, true)
  252. "_handle_action_missing"
  253. end
  254. end
  255. # Checks if the action name is valid and returns false otherwise.
  256. 1 def _valid_action_name?(action_name)
  257. 1 !action_name.to_s.include? File::SEPARATOR
  258. end
  259. end
  260. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/caching.rb

80.56% lines covered

36 relevant lines. 29 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module AbstractController
  3. 1 module Caching
  4. 1 extend ActiveSupport::Concern
  5. 1 extend ActiveSupport::Autoload
  6. 1 eager_autoload do
  7. 1 autoload :Fragments
  8. end
  9. 1 module ConfigMethods
  10. 1 def cache_store
  11. config.cache_store
  12. end
  13. 1 def cache_store=(store)
  14. 2 config.cache_store = ActiveSupport::Cache.lookup_store(*store)
  15. end
  16. 1 private
  17. 1 def cache_configured?
  18. perform_caching && cache_store
  19. end
  20. end
  21. 1 include ConfigMethods
  22. 1 include AbstractController::Caching::Fragments
  23. 1 included do
  24. 2 extend ConfigMethods
  25. 2 config_accessor :default_static_extension
  26. 2 self.default_static_extension ||= ".html"
  27. 2 config_accessor :perform_caching
  28. 2 self.perform_caching = true if perform_caching.nil?
  29. 2 config_accessor :enable_fragment_cache_logging
  30. 2 self.enable_fragment_cache_logging = false
  31. 2 class_attribute :_view_cache_dependencies, default: []
  32. 2 helper_method :view_cache_dependencies if respond_to?(:helper_method)
  33. end
  34. 1 module ClassMethods
  35. 1 def view_cache_dependency(&dependency)
  36. self._view_cache_dependencies += [dependency]
  37. end
  38. end
  39. 1 def view_cache_dependencies
  40. self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
  41. end
  42. 1 private
  43. # Convenience accessor.
  44. 1 def cache(key, options = {}, &block) # :doc:
  45. if cache_configured?
  46. cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
  47. else
  48. yield
  49. end
  50. end
  51. end
  52. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/caching/fragments.rb

37.5% lines covered

48 relevant lines. 18 lines covered and 30 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module AbstractController
  3. 1 module Caching
  4. # Fragment caching is used for caching various blocks within
  5. # views without caching the entire action as a whole. This is
  6. # useful when certain elements of an action change frequently or
  7. # depend on complicated state while other parts rarely change or
  8. # can be shared amongst multiple parties. The caching is done using
  9. # the +cache+ helper available in the Action View. See
  10. # ActionView::Helpers::CacheHelper for more information.
  11. #
  12. # While it's strongly recommended that you use key-based cache
  13. # expiration (see links in CacheHelper for more information),
  14. # it is also possible to manually expire caches. For example:
  15. #
  16. # expire_fragment('name_of_cache')
  17. 1 module Fragments
  18. 1 extend ActiveSupport::Concern
  19. 1 included do
  20. 2 if respond_to?(:class_attribute)
  21. 2 class_attribute :fragment_cache_keys
  22. else
  23. mattr_writer :fragment_cache_keys
  24. end
  25. 2 self.fragment_cache_keys = []
  26. 2 if respond_to?(:helper_method)
  27. 2 helper_method :combined_fragment_cache_key
  28. end
  29. end
  30. 1 module ClassMethods
  31. # Allows you to specify controller-wide key prefixes for
  32. # cache fragments. Pass either a constant +value+, or a block
  33. # which computes a value each time a cache key is generated.
  34. #
  35. # For example, you may want to prefix all fragment cache keys
  36. # with a global version identifier, so you can easily
  37. # invalidate all caches.
  38. #
  39. # class ApplicationController
  40. # fragment_cache_key "v1"
  41. # end
  42. #
  43. # When it's time to invalidate all fragments, simply change
  44. # the string constant. Or, progressively roll out the cache
  45. # invalidation using a computed value:
  46. #
  47. # class ApplicationController
  48. # fragment_cache_key do
  49. # @account.id.odd? ? "v1" : "v2"
  50. # end
  51. # end
  52. 1 def fragment_cache_key(value = nil, &key)
  53. self.fragment_cache_keys += [key || -> { value }]
  54. end
  55. end
  56. # Given a key (as described in +expire_fragment+), returns
  57. # a key array suitable for use in reading, writing, or expiring a
  58. # cached fragment. All keys begin with <tt>:views</tt>,
  59. # followed by <tt>ENV["RAILS_CACHE_ID"]</tt> or <tt>ENV["RAILS_APP_VERSION"]</tt> if set,
  60. # followed by any controller-wide key prefix values, ending
  61. # with the specified +key+ value.
  62. 1 def combined_fragment_cache_key(key)
  63. head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
  64. tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
  65. cache_key = [:views, ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"], head, tail]
  66. cache_key.flatten!(1)
  67. cache_key.compact!
  68. cache_key
  69. end
  70. # Writes +content+ to the location signified by
  71. # +key+ (see +expire_fragment+ for acceptable formats).
  72. 1 def write_fragment(key, content, options = nil)
  73. return content unless cache_configured?
  74. key = combined_fragment_cache_key(key)
  75. instrument_fragment_cache :write_fragment, key do
  76. content = content.to_str
  77. cache_store.write(key, content, options)
  78. end
  79. content
  80. end
  81. # Reads a cached fragment from the location signified by +key+
  82. # (see +expire_fragment+ for acceptable formats).
  83. 1 def read_fragment(key, options = nil)
  84. return unless cache_configured?
  85. key = combined_fragment_cache_key(key)
  86. instrument_fragment_cache :read_fragment, key do
  87. result = cache_store.read(key, options)
  88. result.respond_to?(:html_safe) ? result.html_safe : result
  89. end
  90. end
  91. # Check if a cached fragment from the location signified by
  92. # +key+ exists (see +expire_fragment+ for acceptable formats).
  93. 1 def fragment_exist?(key, options = nil)
  94. return unless cache_configured?
  95. key = combined_fragment_cache_key(key)
  96. instrument_fragment_cache :exist_fragment?, key do
  97. cache_store.exist?(key, options)
  98. end
  99. end
  100. # Removes fragments from the cache.
  101. #
  102. # +key+ can take one of three forms:
  103. #
  104. # * String - This would normally take the form of a path, like
  105. # <tt>pages/45/notes</tt>.
  106. # * Hash - Treated as an implicit call to +url_for+, like
  107. # <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
  108. # * Regexp - Will remove any fragment that matches, so
  109. # <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
  110. # don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
  111. # the actual filename matched looks like
  112. # <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
  113. # only supported on caches that can iterate over all keys (unlike
  114. # memcached).
  115. #
  116. # +options+ is passed through to the cache store's +delete+
  117. # method (or <tt>delete_matched</tt>, for Regexp keys).
  118. 1 def expire_fragment(key, options = nil)
  119. return unless cache_configured?
  120. key = combined_fragment_cache_key(key) unless key.is_a?(Regexp)
  121. instrument_fragment_cache :expire_fragment, key do
  122. if key.is_a?(Regexp)
  123. cache_store.delete_matched(key, options)
  124. else
  125. cache_store.delete(key, options)
  126. end
  127. end
  128. end
  129. 1 def instrument_fragment_cache(name, key) # :nodoc:
  130. ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key)) { yield }
  131. end
  132. end
  133. end
  134. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/callbacks.rb

86.11% lines covered

36 relevant lines. 31 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module AbstractController
  3. # = Abstract Controller Callbacks
  4. #
  5. # Abstract Controller provides hooks during the life cycle of a controller action.
  6. # Callbacks allow you to trigger logic during this cycle. Available callbacks are:
  7. #
  8. # * <tt>after_action</tt>
  9. # * <tt>append_after_action</tt>
  10. # * <tt>append_around_action</tt>
  11. # * <tt>append_before_action</tt>
  12. # * <tt>around_action</tt>
  13. # * <tt>before_action</tt>
  14. # * <tt>prepend_after_action</tt>
  15. # * <tt>prepend_around_action</tt>
  16. # * <tt>prepend_before_action</tt>
  17. # * <tt>skip_after_action</tt>
  18. # * <tt>skip_around_action</tt>
  19. # * <tt>skip_before_action</tt>
  20. #
  21. # NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
  22. #
  23. 1 module Callbacks
  24. 1 extend ActiveSupport::Concern
  25. # Uses ActiveSupport::Callbacks as the base functionality. For
  26. # more details on the whole callback system, read the documentation
  27. # for ActiveSupport::Callbacks.
  28. 1 include ActiveSupport::Callbacks
  29. 1 included do
  30. 3 define_callbacks :process_action,
  31. terminator: ->(controller, result_lambda) { result_lambda.call; controller.performed? },
  32. skip_after_callbacks_if_terminated: true
  33. end
  34. # Override <tt>AbstractController::Base#process_action</tt> to run the
  35. # <tt>process_action</tt> callbacks around the normal behavior.
  36. 1 def process_action(*)
  37. 1 run_callbacks(:process_action) do
  38. 1 super
  39. end
  40. end
  41. 1 module ClassMethods
  42. # If +:only+ or +:except+ are used, convert the options into the
  43. # +:if+ and +:unless+ options of ActiveSupport::Callbacks.
  44. #
  45. # The basic idea is that <tt>:only => :index</tt> gets converted to
  46. # <tt>:if => proc {|c| c.action_name == "index" }</tt>.
  47. #
  48. # Note that <tt>:only</tt> has priority over <tt>:if</tt> in case they
  49. # are used together.
  50. #
  51. # only: :index, if: -> { true } # the :if option will be ignored.
  52. #
  53. # Note that <tt>:if</tt> has priority over <tt>:except</tt> in case they
  54. # are used together.
  55. #
  56. # except: :index, if: -> { true } # the :except option will be ignored.
  57. #
  58. # ==== Options
  59. # * <tt>only</tt> - The callback should be run only for this action.
  60. # * <tt>except</tt> - The callback should be run for all actions except this action.
  61. 1 def _normalize_callback_options(options)
  62. 5 _normalize_callback_option(options, :only, :if)
  63. 5 _normalize_callback_option(options, :except, :unless)
  64. end
  65. 1 def _normalize_callback_option(options, from, to) # :nodoc:
  66. 10 if from = options.delete(from)
  67. 1 _from = Array(from).map(&:to_s).to_set
  68. 2 from = proc { |c| _from.include? c.action_name }
  69. 1 options[to] = Array(options[to]).unshift(from)
  70. end
  71. end
  72. # Take callback names and an optional callback proc, normalize them,
  73. # then call the block with each callback. This allows us to abstract
  74. # the normalization across several methods that use it.
  75. #
  76. # ==== Parameters
  77. # * <tt>callbacks</tt> - An array of callbacks, with an optional
  78. # options hash as the last parameter.
  79. # * <tt>block</tt> - A proc that should be added to the callbacks.
  80. #
  81. # ==== Block Parameters
  82. # * <tt>name</tt> - The callback to be added.
  83. # * <tt>options</tt> - A hash of options to be used when adding the callback.
  84. 1 def _insert_callbacks(callbacks, block = nil)
  85. 5 options = callbacks.extract_options!
  86. 5 _normalize_callback_options(options)
  87. 5 callbacks.push(block) if block
  88. 5 callbacks.each do |callback|
  89. 5 yield callback, options
  90. end
  91. end
  92. ##
  93. # :method: before_action
  94. #
  95. # :call-seq: before_action(names, block)
  96. #
  97. # Append a callback before actions. See _insert_callbacks for parameter details.
  98. #
  99. # If the callback renders or redirects, the action will not run. If there
  100. # are additional callbacks scheduled to run after that callback, they are
  101. # also cancelled.
  102. ##
  103. # :method: prepend_before_action
  104. #
  105. # :call-seq: prepend_before_action(names, block)
  106. #
  107. # Prepend a callback before actions. See _insert_callbacks for parameter details.
  108. #
  109. # If the callback renders or redirects, the action will not run. If there
  110. # are additional callbacks scheduled to run after that callback, they are
  111. # also cancelled.
  112. ##
  113. # :method: skip_before_action
  114. #
  115. # :call-seq: skip_before_action(names)
  116. #
  117. # Skip a callback before actions. See _insert_callbacks for parameter details.
  118. ##
  119. # :method: append_before_action
  120. #
  121. # :call-seq: append_before_action(names, block)
  122. #
  123. # Append a callback before actions. See _insert_callbacks for parameter details.
  124. #
  125. # If the callback renders or redirects, the action will not run. If there
  126. # are additional callbacks scheduled to run after that callback, they are
  127. # also cancelled.
  128. ##
  129. # :method: after_action
  130. #
  131. # :call-seq: after_action(names, block)
  132. #
  133. # Append a callback after actions. See _insert_callbacks for parameter details.
  134. ##
  135. # :method: prepend_after_action
  136. #
  137. # :call-seq: prepend_after_action(names, block)
  138. #
  139. # Prepend a callback after actions. See _insert_callbacks for parameter details.
  140. ##
  141. # :method: skip_after_action
  142. #
  143. # :call-seq: skip_after_action(names)
  144. #
  145. # Skip a callback after actions. See _insert_callbacks for parameter details.
  146. ##
  147. # :method: append_after_action
  148. #
  149. # :call-seq: append_after_action(names, block)
  150. #
  151. # Append a callback after actions. See _insert_callbacks for parameter details.
  152. ##
  153. # :method: around_action
  154. #
  155. # :call-seq: around_action(names, block)
  156. #
  157. # Append a callback around actions. See _insert_callbacks for parameter details.
  158. ##
  159. # :method: prepend_around_action
  160. #
  161. # :call-seq: prepend_around_action(names, block)
  162. #
  163. # Prepend a callback around actions. See _insert_callbacks for parameter details.
  164. ##
  165. # :method: skip_around_action
  166. #
  167. # :call-seq: skip_around_action(names)
  168. #
  169. # Skip a callback around actions. See _insert_callbacks for parameter details.
  170. ##
  171. # :method: append_around_action
  172. #
  173. # :call-seq: append_around_action(names, block)
  174. #
  175. # Append a callback around actions. See _insert_callbacks for parameter details.
  176. # set up before_action, prepend_before_action, skip_before_action, etc.
  177. # for each of before, after, and around.
  178. 1 [:before, :after, :around].each do |callback|
  179. 3 define_method "#{callback}_action" do |*names, &blk|
  180. 5 _insert_callbacks(names, blk) do |name, options|
  181. 5 set_callback(:process_action, callback, name, options)
  182. end
  183. end
  184. 3 define_method "prepend_#{callback}_action" do |*names, &blk|
  185. _insert_callbacks(names, blk) do |name, options|
  186. set_callback(:process_action, callback, name, options.merge(prepend: true))
  187. end
  188. end
  189. # Skip a before, after or around callback. See _insert_callbacks
  190. # for details on the allowed parameters.
  191. 3 define_method "skip_#{callback}_action" do |*names|
  192. _insert_callbacks(names) do |name, options|
  193. skip_callback(:process_action, callback, name, options)
  194. end
  195. end
  196. # *_action is the same as append_*_action
  197. 3 alias_method :"append_#{callback}_action", :"#{callback}_action"
  198. end
  199. end
  200. end
  201. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/collector.rb

61.11% lines covered

18 relevant lines. 11 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_dispatch/http/mime_type"
  3. 1 module AbstractController
  4. 1 module Collector
  5. 1 def self.generate_method_for_mime(mime)
  6. 34 sym = mime.is_a?(Symbol) ? mime : mime.to_sym
  7. 34 class_eval <<-RUBY, __FILE__, __LINE__ + 1
  8. def #{sym}(*args, &block)
  9. custom(Mime[:#{sym}], *args, &block)
  10. end
  11. RUBY
  12. end
  13. 1 Mime::SET.each do |mime|
  14. 34 generate_method_for_mime(mime)
  15. end
  16. 1 Mime::Type.register_callback do |mime|
  17. generate_method_for_mime(mime) unless instance_methods.include?(mime.to_sym)
  18. end
  19. 1 private
  20. 1 def method_missing(symbol, &block)
  21. unless mime_constant = Mime[symbol]
  22. raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
  23. "https://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
  24. "If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
  25. "be sure to nest your variant response within a format response: " \
  26. "format.html { |html| html.tablet { ... } }"
  27. end
  28. if Mime::SET.include?(mime_constant)
  29. AbstractController::Collector.generate_method_for_mime(mime_constant)
  30. send(symbol, &block)
  31. else
  32. super
  33. end
  34. end
  35. end
  36. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/error.rb

100.0% lines covered

2 relevant lines. 2 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module AbstractController
  3. 1 class Error < StandardError #:nodoc:
  4. end
  5. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/logger.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/benchmarkable"
  3. 1 module AbstractController
  4. 1 module Logger #:nodoc:
  5. 1 extend ActiveSupport::Concern
  6. 1 included do
  7. 3 config_accessor :logger
  8. 3 include ActiveSupport::Benchmarkable
  9. end
  10. end
  11. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/rendering.rb

78.57% lines covered

56 relevant lines. 44 lines covered and 12 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "abstract_controller/error"
  3. 1 require "action_view"
  4. 1 require "action_view/view_paths"
  5. 1 require "set"
  6. 1 module AbstractController
  7. 1 class DoubleRenderError < Error
  8. 1 DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
  9. 1 def initialize(message = nil)
  10. super(message || DEFAULT_MESSAGE)
  11. end
  12. end
  13. 1 module Rendering
  14. 1 extend ActiveSupport::Concern
  15. 1 include ActionView::ViewPaths
  16. # Normalizes arguments, options and then delegates render_to_body and
  17. # sticks the result in <tt>self.response_body</tt>.
  18. 1 def render(*args, &block)
  19. 1 options = _normalize_render(*args, &block)
  20. 1 rendered_body = render_to_body(options)
  21. 1 if options[:html]
  22. _set_html_content_type
  23. else
  24. 1 _set_rendered_content_type rendered_format
  25. end
  26. 1 _set_vary_header
  27. 1 self.response_body = rendered_body
  28. end
  29. # Raw rendering of a template to a string.
  30. #
  31. # It is similar to render, except that it does not
  32. # set the +response_body+ and it should be guaranteed
  33. # to always return a string.
  34. #
  35. # If a component extends the semantics of +response_body+
  36. # (as ActionController extends it to be anything that
  37. # responds to the method each), this method needs to be
  38. # overridden in order to still return a string.
  39. 1 def render_to_string(*args, &block)
  40. options = _normalize_render(*args, &block)
  41. render_to_body(options)
  42. end
  43. # Performs the actual template rendering.
  44. 1 def render_to_body(options = {})
  45. end
  46. # Returns Content-Type of rendered content.
  47. 1 def rendered_format
  48. 1 Mime[:text]
  49. end
  50. 1 DEFAULT_PROTECTED_INSTANCE_VARIABLES = %i(@_action_name @_response_body @_formats @_prefixes)
  51. # This method should return a hash with assigns.
  52. # You can overwrite this configuration per controller.
  53. 1 def view_assigns
  54. variables = instance_variables - _protected_ivars
  55. variables.each_with_object({}) do |name, hash|
  56. hash[name.slice(1, name.length)] = instance_variable_get(name)
  57. end
  58. end
  59. 1 private
  60. # Normalize args by converting <tt>render "foo"</tt> to
  61. # <tt>render :action => "foo"</tt> and <tt>render "foo/bar"</tt> to
  62. # <tt>render :file => "foo/bar"</tt>.
  63. 1 def _normalize_args(action = nil, options = {}) # :doc:
  64. 1 if action.respond_to?(:permitted?)
  65. if action.permitted?
  66. action
  67. else
  68. raise ArgumentError, "render parameters are not permitted"
  69. end
  70. 1 elsif action.is_a?(Hash)
  71. 1 action
  72. else
  73. options
  74. end
  75. end
  76. # Normalize options.
  77. 1 def _normalize_options(options) # :doc:
  78. 1 options
  79. end
  80. # Process extra options.
  81. 1 def _process_options(options) # :doc:
  82. 1 options
  83. end
  84. # Process the rendered format.
  85. 1 def _process_format(format) # :nodoc:
  86. end
  87. 1 def _process_variant(options)
  88. end
  89. 1 def _set_html_content_type # :nodoc:
  90. end
  91. 1 def _set_vary_header # :nodoc:
  92. end
  93. 1 def _set_rendered_content_type(format) # :nodoc:
  94. end
  95. # Normalize args and options.
  96. 1 def _normalize_render(*args, &block) # :nodoc:
  97. 1 options = _normalize_args(*args, &block)
  98. 1 _process_variant(options)
  99. 1 _normalize_options(options)
  100. 1 options
  101. end
  102. 1 def _protected_ivars
  103. DEFAULT_PROTECTED_INSTANCE_VARIABLES
  104. end
  105. end
  106. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/translation.rb

47.06% lines covered

17 relevant lines. 8 lines covered and 9 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/symbol/starts_ends_with"
  3. 1 module AbstractController
  4. 1 module Translation
  5. 1 mattr_accessor :raise_on_missing_translations, default: false
  6. # Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>.
  7. #
  8. # When the given key starts with a period, it will be scoped by the current
  9. # controller and action. So if you call <tt>translate(".foo")</tt> from
  10. # <tt>PeopleController#index</tt>, it will convert the call to
  11. # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
  12. # to translate many keys within the same controller / action and gives you a
  13. # simple framework for scoping them consistently.
  14. 1 def translate(key, **options)
  15. if key&.start_with?(".")
  16. path = controller_path.tr("/", ".")
  17. defaults = [:"#{path}#{key}"]
  18. defaults << options[:default] if options[:default]
  19. options[:default] = defaults.flatten
  20. key = "#{path}.#{action_name}#{key}"
  21. end
  22. i18n_raise = options.fetch(:raise, self.raise_on_missing_translations)
  23. I18n.translate(key, **options, raise: i18n_raise)
  24. end
  25. 1 alias :t :translate
  26. # Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>.
  27. 1 def localize(object, **options)
  28. I18n.localize(object, **options)
  29. end
  30. 1 alias :l :localize
  31. end
  32. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/abstract_controller/url_for.rb

84.62% lines covered

13 relevant lines. 11 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module AbstractController
  3. # Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
  4. # has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
  5. # exception will be raised.
  6. #
  7. # Note that this module is completely decoupled from HTTP - the only requirement is a valid
  8. # <tt>_routes</tt> implementation.
  9. 1 module UrlFor
  10. 1 extend ActiveSupport::Concern
  11. 1 include ActionDispatch::Routing::UrlFor
  12. 1 def _routes
  13. raise "In order to use #url_for, you must include routing helpers explicitly. " \
  14. "For instance, `include Rails.application.routes.url_helpers`."
  15. end
  16. 1 module ClassMethods
  17. 1 def _routes
  18. nil
  19. end
  20. 1 def action_methods
  21. 1 @action_methods ||= begin
  22. 1 if _routes
  23. 1 super - _routes.named_routes.helper_names
  24. else
  25. super
  26. end
  27. end
  28. end
  29. end
  30. end
  31. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/api.rb

80.0% lines covered

15 relevant lines. 12 lines covered and 3 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_view"
  3. 1 require "action_controller"
  4. 1 require "action_controller/log_subscriber"
  5. 1 module ActionController
  6. # API Controller is a lightweight version of <tt>ActionController::Base</tt>,
  7. # created for applications that don't require all functionalities that a complete
  8. # \Rails controller provides, allowing you to create controllers with just the
  9. # features that you need for API only applications.
  10. #
  11. # An API Controller is different from a normal controller in the sense that
  12. # by default it doesn't include a number of features that are usually required
  13. # by browser access only: layouts and templates rendering,
  14. # flash, assets, and so on. This makes the entire controller stack thinner,
  15. # suitable for API applications. It doesn't mean you won't have such
  16. # features if you need them: they're all available for you to include in
  17. # your application, they're just not part of the default API controller stack.
  18. #
  19. # Normally, +ApplicationController+ is the only controller that inherits from
  20. # <tt>ActionController::API</tt>. All other controllers in turn inherit from
  21. # +ApplicationController+.
  22. #
  23. # A sample controller could look like this:
  24. #
  25. # class PostsController < ApplicationController
  26. # def index
  27. # posts = Post.all
  28. # render json: posts
  29. # end
  30. # end
  31. #
  32. # Request, response, and parameters objects all work the exact same way as
  33. # <tt>ActionController::Base</tt>.
  34. #
  35. # == Renders
  36. #
  37. # The default API Controller stack includes all renderers, which means you
  38. # can use <tt>render :json</tt> and brothers freely in your controllers. Keep
  39. # in mind that templates are not going to be rendered, so you need to ensure
  40. # your controller is calling either <tt>render</tt> or <tt>redirect_to</tt> in
  41. # all actions, otherwise it will return 204 No Content.
  42. #
  43. # def show
  44. # post = Post.find(params[:id])
  45. # render json: post
  46. # end
  47. #
  48. # == Redirects
  49. #
  50. # Redirects are used to move from one action to another. You can use the
  51. # <tt>redirect_to</tt> method in your controllers in the same way as in
  52. # <tt>ActionController::Base</tt>. For example:
  53. #
  54. # def create
  55. # redirect_to root_url and return if not_authorized?
  56. # # do stuff here
  57. # end
  58. #
  59. # == Adding New Behavior
  60. #
  61. # In some scenarios you may want to add back some functionality provided by
  62. # <tt>ActionController::Base</tt> that is not present by default in
  63. # <tt>ActionController::API</tt>, for instance <tt>MimeResponds</tt>. This
  64. # module gives you the <tt>respond_to</tt> method. Adding it is quite simple,
  65. # you just need to include the module in a specific controller or in
  66. # +ApplicationController+ in case you want it available in your entire
  67. # application:
  68. #
  69. # class ApplicationController < ActionController::API
  70. # include ActionController::MimeResponds
  71. # end
  72. #
  73. # class PostsController < ApplicationController
  74. # def index
  75. # posts = Post.all
  76. #
  77. # respond_to do |format|
  78. # format.json { render json: posts }
  79. # format.xml { render xml: posts }
  80. # end
  81. # end
  82. # end
  83. #
  84. # Make sure to check the modules included in <tt>ActionController::Base</tt>
  85. # if you want to use any other functionality that is not provided
  86. # by <tt>ActionController::API</tt> out of the box.
  87. 1 class API < Metal
  88. 1 abstract!
  89. # Shortcut helper that returns all the ActionController::API modules except
  90. # the ones passed as arguments:
  91. #
  92. # class MyAPIBaseController < ActionController::Metal
  93. # ActionController::API.without_modules(:UrlFor).each do |left|
  94. # include left
  95. # end
  96. # end
  97. #
  98. # This gives better control over what you want to exclude and makes it easier
  99. # to create an API controller class, instead of listing the modules required
  100. # manually.
  101. 1 def self.without_modules(*modules)
  102. modules = modules.map do |m|
  103. m.is_a?(Symbol) ? ActionController.const_get(m) : m
  104. end
  105. MODULES - modules
  106. end
  107. MODULES = [
  108. 1 AbstractController::Rendering,
  109. UrlFor,
  110. Redirecting,
  111. ApiRendering,
  112. Renderers::All,
  113. ConditionalGet,
  114. BasicImplicitRender,
  115. StrongParameters,
  116. DataStreaming,
  117. DefaultHeaders,
  118. Logging,
  119. # Before callbacks should also be executed as early as possible, so
  120. # also include them at the bottom.
  121. AbstractController::Callbacks,
  122. # Append rescue at the bottom to wrap as much as possible.
  123. Rescue,
  124. # Add instrumentations hooks at the bottom, to ensure they instrument
  125. # all the methods properly.
  126. Instrumentation,
  127. # Params wrapper should come before instrumentation so they are
  128. # properly showed in logs
  129. ParamsWrapper
  130. ]
  131. 1 MODULES.each do |mod|
  132. 15 include mod
  133. end
  134. 1 ActiveSupport.run_load_hooks(:action_controller_api, self)
  135. 1 ActiveSupport.run_load_hooks(:action_controller, self)
  136. end
  137. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/api/api_rendering.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 module ApiRendering
  4. 1 extend ActiveSupport::Concern
  5. 1 included do
  6. 1 include Rendering
  7. end
  8. 1 def render_to_body(options = {})
  9. _process_options(options)
  10. super
  11. end
  12. end
  13. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/base.rb

80.0% lines covered

20 relevant lines. 16 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_view"
  3. 1 require "action_controller/log_subscriber"
  4. 1 require "action_controller/metal/params_wrapper"
  5. 1 module ActionController
  6. # Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
  7. # on request and then either it renders a template or redirects to another action. An action is defined as a public method
  8. # on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
  9. #
  10. # By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
  11. # controllers inherit from ApplicationController. This gives you one class to configure things such as
  12. # request forgery protection and filtering of sensitive request parameters.
  13. #
  14. # A sample controller could look like this:
  15. #
  16. # class PostsController < ApplicationController
  17. # def index
  18. # @posts = Post.all
  19. # end
  20. #
  21. # def create
  22. # @post = Post.create params[:post]
  23. # redirect_to posts_path
  24. # end
  25. # end
  26. #
  27. # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
  28. # after executing code in the action. For example, the +index+ action of the PostsController would render the
  29. # template <tt>app/views/posts/index.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
  30. #
  31. # Unlike index, the create action will not render a template. After performing its main purpose (creating a
  32. # new post), it initiates a redirect instead. This redirect works by returning an external
  33. # <tt>302 Moved</tt> HTTP response that takes the user to the index action.
  34. #
  35. # These two methods represent the two basic action archetypes used in Action Controllers: Get-and-show and do-and-redirect.
  36. # Most actions are variations on these themes.
  37. #
  38. # == Requests
  39. #
  40. # For every request, the router determines the value of the +controller+ and +action+ keys. These determine which controller
  41. # and action are called. The remaining request parameters, the session (if one is available), and the full request with
  42. # all the HTTP headers are made available to the action through accessor methods. Then the action is performed.
  43. #
  44. # The full request object is available via the request accessor and is primarily used to query for HTTP headers:
  45. #
  46. # def server_ip
  47. # location = request.env["REMOTE_ADDR"]
  48. # render plain: "This server hosted at #{location}"
  49. # end
  50. #
  51. # == Parameters
  52. #
  53. # All request parameters, whether they come from a query string in the URL or form data submitted through a POST request are
  54. # available through the <tt>params</tt> method which returns a hash. For example, an action that was performed through
  55. # <tt>/posts?category=All&limit=5</tt> will include <tt>{ "category" => "All", "limit" => "5" }</tt> in <tt>params</tt>.
  56. #
  57. # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
  58. #
  59. # <input type="text" name="post[name]" value="david">
  60. # <input type="text" name="post[address]" value="hyacintvej">
  61. #
  62. # A request coming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
  63. # If the address input had been named <tt>post[address][street]</tt>, the <tt>params</tt> would have included
  64. # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
  65. #
  66. # == Sessions
  67. #
  68. # Sessions allow you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
  69. # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
  70. # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
  71. # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
  72. #
  73. # You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
  74. #
  75. # session[:person] = Person.authenticate(user_name, password)
  76. #
  77. # You can retrieve it again through the same hash:
  78. #
  79. # "Hello #{session[:person]}"
  80. #
  81. # For removing objects from the session, you can either assign a single key to +nil+:
  82. #
  83. # # removes :person from session
  84. # session[:person] = nil
  85. #
  86. # or you can remove the entire session with +reset_session+.
  87. #
  88. # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
  89. # This prevents the user from tampering with the session but also allows them to see its contents.
  90. #
  91. # Do not put secret information in cookie-based sessions!
  92. #
  93. # == Responses
  94. #
  95. # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
  96. # object is generated automatically through the use of renders and redirects and requires no user intervention.
  97. #
  98. # == Renders
  99. #
  100. # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
  101. # of a template. Included in the Action Pack is the Action View, which enables rendering of ERB templates. It's automatically configured.
  102. # The controller passes objects to the view by assigning instance variables:
  103. #
  104. # def show
  105. # @post = Post.find(params[:id])
  106. # end
  107. #
  108. # Which are then automatically available to the view:
  109. #
  110. # Title: <%= @post.title %>
  111. #
  112. # You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
  113. # will use the manual rendering methods:
  114. #
  115. # def search
  116. # @results = Search.find(params[:query])
  117. # case @results.count
  118. # when 0 then render action: "no_results"
  119. # when 1 then render action: "show"
  120. # when 2..10 then render action: "show_many"
  121. # end
  122. # end
  123. #
  124. # Read more about writing ERB and Builder templates in ActionView::Base.
  125. #
  126. # == Redirects
  127. #
  128. # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the
  129. # database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
  130. # going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
  131. #
  132. # def create
  133. # @entry = Entry.new(params[:entry])
  134. # if @entry.save
  135. # # The entry was saved correctly, redirect to show
  136. # redirect_to action: 'show', id: @entry.id
  137. # else
  138. # # things didn't go so well, do something else
  139. # end
  140. # end
  141. #
  142. # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method, which is then executed.
  143. # Note that this is an external HTTP-level redirection which will cause the browser to make a second request (a GET to the show action),
  144. # and not some internal re-routing which calls both "create" and then "show" within one request.
  145. #
  146. # Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
  147. #
  148. # == Calling multiple redirects or renders
  149. #
  150. # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
  151. #
  152. # def do_something
  153. # redirect_to action: "elsewhere"
  154. # render action: "overthere" # raises DoubleRenderError
  155. # end
  156. #
  157. # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
  158. #
  159. # def do_something
  160. # redirect_to(action: "elsewhere") and return if monkeys.nil?
  161. # render action: "overthere" # won't be called if monkeys is nil
  162. # end
  163. #
  164. 1 class Base < Metal
  165. 1 abstract!
  166. # We document the request and response methods here because albeit they are
  167. # implemented in ActionController::Metal, the type of the returned objects
  168. # is unknown at that level.
  169. ##
  170. # :method: request
  171. #
  172. # Returns an ActionDispatch::Request instance that represents the
  173. # current request.
  174. ##
  175. # :method: response
  176. #
  177. # Returns an ActionDispatch::Response that represents the current
  178. # response.
  179. # Shortcut helper that returns all the modules included in
  180. # ActionController::Base except the ones passed as arguments:
  181. #
  182. # class MyBaseController < ActionController::Metal
  183. # ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
  184. # include left
  185. # end
  186. # end
  187. #
  188. # This gives better control over what you want to exclude and makes it
  189. # easier to create a bare controller class, instead of listing the modules
  190. # required manually.
  191. 1 def self.without_modules(*modules)
  192. modules = modules.map do |m|
  193. m.is_a?(Symbol) ? ActionController.const_get(m) : m
  194. end
  195. MODULES - modules
  196. end
  197. MODULES = [
  198. 1 AbstractController::Rendering,
  199. AbstractController::Translation,
  200. AbstractController::AssetPaths,
  201. Helpers,
  202. UrlFor,
  203. Redirecting,
  204. ActionView::Layouts,
  205. Rendering,
  206. Renderers::All,
  207. ConditionalGet,
  208. EtagWithTemplateDigest,
  209. EtagWithFlash,
  210. Caching,
  211. MimeResponds,
  212. ImplicitRender,
  213. StrongParameters,
  214. ParameterEncoding,
  215. Cookies,
  216. Flash,
  217. FormBuilder,
  218. RequestForgeryProtection,
  219. ContentSecurityPolicy,
  220. PermissionsPolicy,
  221. Streaming,
  222. DataStreaming,
  223. HttpAuthentication::Basic::ControllerMethods,
  224. HttpAuthentication::Digest::ControllerMethods,
  225. HttpAuthentication::Token::ControllerMethods,
  226. DefaultHeaders,
  227. Logging,
  228. # Before callbacks should also be executed as early as possible, so
  229. # also include them at the bottom.
  230. AbstractController::Callbacks,
  231. # Append rescue at the bottom to wrap as much as possible.
  232. Rescue,
  233. # Add instrumentations hooks at the bottom, to ensure they instrument
  234. # all the methods properly.
  235. Instrumentation,
  236. # Params wrapper should come before instrumentation so they are
  237. # properly showed in logs
  238. ParamsWrapper
  239. ]
  240. 1 MODULES.each do |mod|
  241. 34 include mod
  242. end
  243. 1 setup_renderer!
  244. # Define some internal variables that should not be propagated to the view.
  245. 1 PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + %i(
  246. @_params @_response @_request @_config @_url_options @_action_has_layout @_view_context_class
  247. @_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy
  248. )
  249. 1 def _protected_ivars
  250. PROTECTED_IVARS
  251. end
  252. 1 private :_protected_ivars
  253. 1 ActiveSupport.run_load_hooks(:action_controller_base, self)
  254. 1 ActiveSupport.run_load_hooks(:action_controller, self)
  255. end
  256. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/caching.rb

80.0% lines covered

10 relevant lines. 8 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # \Caching is a cheap way of speeding up slow applications by keeping the result of
  4. # calculations, renderings, and database calls around for subsequent requests.
  5. #
  6. # You can read more about each approach by clicking the modules below.
  7. #
  8. # Note: To turn off all caching provided by Action Controller, set
  9. # config.action_controller.perform_caching = false
  10. #
  11. # == \Caching stores
  12. #
  13. # All the caching stores from ActiveSupport::Cache are available to be used as backends
  14. # for Action Controller caching.
  15. #
  16. # Configuration examples (FileStore is the default):
  17. #
  18. # config.action_controller.cache_store = :memory_store
  19. # config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
  20. # config.action_controller.cache_store = :mem_cache_store, 'localhost'
  21. # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
  22. # config.action_controller.cache_store = MyOwnStore.new('parameter')
  23. 1 module Caching
  24. 1 extend ActiveSupport::Concern
  25. 1 included do
  26. 1 include AbstractController::Caching
  27. end
  28. 1 private
  29. 1 def instrument_payload(key)
  30. {
  31. controller: controller_name,
  32. action: action_name,
  33. key: key
  34. }
  35. end
  36. 1 def instrument_name
  37. "action_controller"
  38. end
  39. end
  40. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/form_builder.rb

80.0% lines covered

10 relevant lines. 8 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # Override the default form builder for all views rendered by this
  4. # controller and any of its descendants. Accepts a subclass of
  5. # +ActionView::Helpers::FormBuilder+.
  6. #
  7. # For example, given a form builder:
  8. #
  9. # class AdminFormBuilder < ActionView::Helpers::FormBuilder
  10. # def special_field(name)
  11. # end
  12. # end
  13. #
  14. # The controller specifies a form builder as its default:
  15. #
  16. # class AdminAreaController < ApplicationController
  17. # default_form_builder AdminFormBuilder
  18. # end
  19. #
  20. # Then in the view any form using +form_for+ will be an instance of the
  21. # specified form builder:
  22. #
  23. # <%= form_for(@instance) do |builder| %>
  24. # <%= builder.special_field(:name) %>
  25. # <% end %>
  26. 1 module FormBuilder
  27. 1 extend ActiveSupport::Concern
  28. 1 included do
  29. 1 class_attribute :_default_form_builder, instance_accessor: false
  30. end
  31. 1 module ClassMethods
  32. # Set the form builder to be used as the default for all forms
  33. # in the views rendered by this controller and its subclasses.
  34. #
  35. # ==== Parameters
  36. # * <tt>builder</tt> - Default form builder, an instance of +ActionView::Helpers::FormBuilder+
  37. 1 def default_form_builder(builder)
  38. self._default_form_builder = builder
  39. end
  40. end
  41. # Default form builder for the controller
  42. 1 def default_form_builder
  43. self.class._default_form_builder
  44. end
  45. end
  46. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/log_subscriber.rb

80.49% lines covered

41 relevant lines. 33 lines covered and 8 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 class LogSubscriber < ActiveSupport::LogSubscriber
  4. 1 INTERNAL_PARAMS = %w(controller action format _method only_path)
  5. 1 def start_processing(event)
  6. 1 return unless logger.info?
  7. 1 payload = event.payload
  8. 1 params = payload[:params].except(*INTERNAL_PARAMS)
  9. 1 format = payload[:format]
  10. 1 format = format.to_s.upcase if format.is_a?(Symbol)
  11. 1 format = "*/*" if format.nil?
  12. 1 info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
  13. 1 info " Parameters: #{params.inspect}" unless params.empty?
  14. end
  15. 1 def process_action(event)
  16. 1 info do
  17. 1 payload = event.payload
  18. 1 additions = ActionController::Base.log_process_action(payload)
  19. 1 status = payload[:status]
  20. 1 if status.nil? && (exception_class_name = payload[:exception]&.first)
  21. status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
  22. end
  23. 1 additions << "Allocations: #{event.allocations}"
  24. 1 message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
  25. 1 message << " (#{additions.join(" | ")})"
  26. 1 message << "\n\n" if defined?(Rails.env) && Rails.env.development?
  27. 1 message
  28. end
  29. end
  30. 1 def halted_callback(event)
  31. info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" }
  32. end
  33. 1 def send_file(event)
  34. info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
  35. end
  36. 1 def redirect_to(event)
  37. info { "Redirected to #{event.payload[:location]}" }
  38. end
  39. 1 def send_data(event)
  40. info { "Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)" }
  41. end
  42. 1 def unpermitted_parameters(event)
  43. debug do
  44. unpermitted_keys = event.payload[:keys]
  45. color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.map { |e| ":#{e}" }.join(", ")}", RED)
  46. end
  47. end
  48. 1 %w(write_fragment read_fragment exist_fragment?
  49. expire_fragment expire_page write_page).each do |method|
  50. 6 class_eval <<-METHOD, __FILE__, __LINE__ + 1
  51. def #{method}(event)
  52. return unless logger.info? && ActionController::Base.enable_fragment_cache_logging
  53. key = ActiveSupport::Cache.expand_cache_key(event.payload[:key] || event.payload[:path])
  54. human_name = #{method.to_s.humanize.inspect}
  55. info("\#{human_name} \#{key} (\#{event.duration.round(1)}ms)")
  56. end
  57. METHOD
  58. end
  59. 1 def logger
  60. 9 ActionController::Base.logger
  61. end
  62. end
  63. end
  64. 1 ActionController::LogSubscriber.attach_to :action_controller

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal.rb

64.15% lines covered

106 relevant lines. 68 lines covered and 38 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/array/extract_options"
  3. 1 require "action_dispatch/middleware/stack"
  4. 1 require "action_dispatch/http/request"
  5. 1 require "action_dispatch/http/response"
  6. 1 module ActionController
  7. # Extend ActionDispatch middleware stack to make it aware of options
  8. # allowing the following syntax in controllers:
  9. #
  10. # class PostsController < ApplicationController
  11. # use AuthenticationMiddleware, except: [:index, :show]
  12. # end
  13. #
  14. 1 class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
  15. 1 class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
  16. 1 def initialize(klass, args, actions, strategy, block)
  17. @actions = actions
  18. @strategy = strategy
  19. super(klass, args, block)
  20. end
  21. 1 def valid?(action)
  22. @strategy.call @actions, action
  23. end
  24. end
  25. 1 def build(action, app = nil, &block)
  26. action = action.to_s
  27. middlewares.reverse.inject(app || block) do |a, middleware|
  28. middleware.valid?(action) ? middleware.build(a) : a
  29. end
  30. end
  31. 1 private
  32. 1 INCLUDE = ->(list, action) { list.include? action }
  33. 1 EXCLUDE = ->(list, action) { !list.include? action }
  34. 1 NULL = ->(list, action) { true }
  35. 1 def build_middleware(klass, args, block)
  36. options = args.extract_options!
  37. only = Array(options.delete(:only)).map(&:to_s)
  38. except = Array(options.delete(:except)).map(&:to_s)
  39. args << options unless options.empty?
  40. strategy = NULL
  41. list = nil
  42. if only.any?
  43. strategy = INCLUDE
  44. list = only
  45. elsif except.any?
  46. strategy = EXCLUDE
  47. list = except
  48. end
  49. Middleware.new(klass, args, list, strategy, block)
  50. end
  51. end
  52. # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
  53. # valid Rack interface without the additional niceties provided by
  54. # <tt>ActionController::Base</tt>.
  55. #
  56. # A sample metal controller might look like this:
  57. #
  58. # class HelloController < ActionController::Metal
  59. # def index
  60. # self.response_body = "Hello World!"
  61. # end
  62. # end
  63. #
  64. # And then to route requests to your metal controller, you would add
  65. # something like this to <tt>config/routes.rb</tt>:
  66. #
  67. # get 'hello', to: HelloController.action(:index)
  68. #
  69. # The +action+ method returns a valid Rack application for the \Rails
  70. # router to dispatch to.
  71. #
  72. # == Rendering Helpers
  73. #
  74. # <tt>ActionController::Metal</tt> by default provides no utilities for rendering
  75. # views, partials, or other responses aside from explicitly calling of
  76. # <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
  77. # add the render helpers you're used to having in a normal controller, you
  78. # can do the following:
  79. #
  80. # class HelloController < ActionController::Metal
  81. # include AbstractController::Rendering
  82. # include ActionView::Layouts
  83. # append_view_path "#{Rails.root}/app/views"
  84. #
  85. # def index
  86. # render "hello/index"
  87. # end
  88. # end
  89. #
  90. # == Redirection Helpers
  91. #
  92. # To add redirection helpers to your metal controller, do the following:
  93. #
  94. # class HelloController < ActionController::Metal
  95. # include ActionController::Redirecting
  96. # include Rails.application.routes.url_helpers
  97. #
  98. # def index
  99. # redirect_to root_url
  100. # end
  101. # end
  102. #
  103. # == Other Helpers
  104. #
  105. # You can refer to the modules included in <tt>ActionController::Base</tt> to see
  106. # other features you can bring into your metal controller.
  107. #
  108. 1 class Metal < AbstractController::Base
  109. 1 abstract!
  110. # Returns the last part of the controller's name, underscored, without the ending
  111. # <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
  112. # Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
  113. #
  114. # ==== Returns
  115. # * <tt>string</tt>
  116. 1 def self.controller_name
  117. @controller_name ||= (name.demodulize.delete_suffix("Controller").underscore unless anonymous?)
  118. end
  119. 1 def self.make_response!(request)
  120. ActionDispatch::Response.new.tap do |res|
  121. res.request = request
  122. end
  123. end
  124. 1 def self.action_encoding_template(action) # :nodoc:
  125. 3 false
  126. end
  127. # Delegates to the class' <tt>controller_name</tt>.
  128. 1 def controller_name
  129. self.class.controller_name
  130. end
  131. 1 attr_internal :response, :request
  132. 1 delegate :session, to: "@_request"
  133. 1 delegate :headers, :status=, :location=, :content_type=,
  134. :status, :location, :content_type, :media_type, to: "@_response"
  135. 1 def initialize
  136. 1 @_request = nil
  137. 1 @_response = nil
  138. 1 @_routes = nil
  139. 1 super
  140. end
  141. 1 def params
  142. @_params ||= request.parameters
  143. end
  144. 1 def params=(val)
  145. @_params = val
  146. end
  147. 1 alias :response_code :status # :nodoc:
  148. # Basic url_for that can be overridden for more robust functionality.
  149. 1 def url_for(string)
  150. string
  151. end
  152. 1 def response_body=(body)
  153. 1 body = [body] unless body.nil? || body.respond_to?(:each)
  154. 1 response.reset_body!
  155. 1 return unless body
  156. 1 response.body = body
  157. 1 super
  158. end
  159. # Tests if render or redirect has already happened.
  160. 1 def performed?
  161. 1 response_body || response.committed?
  162. end
  163. 1 def dispatch(name, request, response) #:nodoc:
  164. 1 set_request!(request)
  165. 1 set_response!(response)
  166. 1 process(name)
  167. 1 request.commit_flash
  168. 1 to_a
  169. end
  170. 1 def set_response!(response) # :nodoc:
  171. 1 @_response = response
  172. end
  173. 1 def set_request!(request) #:nodoc:
  174. 1 @_request = request
  175. 1 @_request.controller_instance = self
  176. end
  177. 1 def to_a #:nodoc:
  178. 1 response.to_a
  179. end
  180. 1 def reset_session
  181. @_request.reset_session
  182. end
  183. 1 class_attribute :middleware_stack, default: ActionController::MiddlewareStack.new
  184. 1 def self.inherited(base) # :nodoc:
  185. 5 base.middleware_stack = middleware_stack.dup
  186. 5 super
  187. end
  188. 1 class << self
  189. # Pushes the given Rack middleware and its arguments to the bottom of the
  190. # middleware stack.
  191. 1 def use(*args, &block)
  192. middleware_stack.use(*args, &block)
  193. end
  194. 1 ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
  195. end
  196. # Alias for +middleware_stack+.
  197. 1 def self.middleware
  198. middleware_stack
  199. end
  200. # Returns a Rack endpoint for the given action name.
  201. 1 def self.action(name)
  202. app = lambda { |env|
  203. req = ActionDispatch::Request.new(env)
  204. res = make_response! req
  205. new.dispatch(name, req, res)
  206. }
  207. if middleware_stack.any?
  208. middleware_stack.build(name, app)
  209. else
  210. app
  211. end
  212. end
  213. # Direct dispatch to the controller. Instantiates the controller, then
  214. # executes the action named +name+.
  215. 1 def self.dispatch(name, req, res)
  216. 1 if middleware_stack.any?
  217. middleware_stack.build(name) { |env| new.dispatch(name, req, res) }.call req.env
  218. else
  219. 1 new.dispatch(name, req, res)
  220. end
  221. end
  222. end
  223. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/basic_implicit_render.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 module BasicImplicitRender # :nodoc:
  4. 1 def send_action(method, *args)
  5. 2 super.tap { default_render unless performed? }
  6. end
  7. 1 def default_render
  8. head :no_content
  9. end
  10. end
  11. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/conditional_get.rb

48.65% lines covered

37 relevant lines. 18 lines covered and 19 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/object/try"
  3. 1 require "active_support/core_ext/integer/time"
  4. 1 module ActionController
  5. 1 module ConditionalGet
  6. 1 extend ActiveSupport::Concern
  7. 1 include Head
  8. 1 included do
  9. 2 class_attribute :etaggers, default: []
  10. end
  11. 1 module ClassMethods
  12. # Allows you to consider additional controller-wide information when generating an ETag.
  13. # For example, if you serve pages tailored depending on who's logged in at the moment, you
  14. # may want to add the current user id to be part of the ETag to prevent unauthorized displaying
  15. # of cached pages.
  16. #
  17. # class InvoicesController < ApplicationController
  18. # etag { current_user&.id }
  19. #
  20. # def show
  21. # # Etag will differ even for the same invoice when it's viewed by a different current_user
  22. # @invoice = Invoice.find(params[:id])
  23. # fresh_when etag: @invoice
  24. # end
  25. # end
  26. 1 def etag(&etagger)
  27. 2 self.etaggers += [etagger]
  28. end
  29. end
  30. # Sets the +etag+, +last_modified+, or both on the response and renders a
  31. # <tt>304 Not Modified</tt> response if the request is already fresh.
  32. #
  33. # === Parameters:
  34. #
  35. # * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
  36. # +:weak_etag+ option.
  37. # * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
  38. # Requests that set If-None-Match header may return a 304 Not Modified
  39. # response if it matches the ETag exactly. A weak ETag indicates semantic
  40. # equivalence, not byte-for-byte equality, so they're good for caching
  41. # HTML pages in browser caches. They can't be used for responses that
  42. # must be byte-identical, like serving Range requests within a PDF file.
  43. # * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
  44. # Requests that set If-None-Match header may return a 304 Not Modified
  45. # response if it matches the ETag exactly. A strong ETag implies exact
  46. # equality: the response must match byte for byte. This is necessary for
  47. # doing Range requests within a large video or PDF file, for example, or
  48. # for compatibility with some CDNs that don't support weak ETags.
  49. # * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
  50. # response. Subsequent requests that set If-Modified-Since may return a
  51. # 304 Not Modified response if last_modified <= If-Modified-Since.
  52. # * <tt>:public</tt> By default the Cache-Control header is private, set this to
  53. # +true+ if you want your application to be cacheable by other devices (proxy caches).
  54. # * <tt>:template</tt> By default, the template digest for the current
  55. # controller/action is included in ETags. If the action renders a
  56. # different template, you can include its digest instead. If the action
  57. # doesn't render a template at all, you can pass <tt>template: false</tt>
  58. # to skip any attempt to check for a template digest.
  59. #
  60. # === Example:
  61. #
  62. # def show
  63. # @article = Article.find(params[:id])
  64. # fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
  65. # end
  66. #
  67. # This will render the show template if the request isn't sending a matching ETag or
  68. # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
  69. #
  70. # You can also just pass a record. In this case +last_modified+ will be set
  71. # by calling +updated_at+ and +etag+ by passing the object itself.
  72. #
  73. # def show
  74. # @article = Article.find(params[:id])
  75. # fresh_when(@article)
  76. # end
  77. #
  78. # You can also pass an object that responds to +maximum+, such as a
  79. # collection of active records. In this case +last_modified+ will be set by
  80. # calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
  81. # most recently updated record) and the +etag+ by passing the object itself.
  82. #
  83. # def index
  84. # @articles = Article.all
  85. # fresh_when(@articles)
  86. # end
  87. #
  88. # When passing a record or a collection, you can still set the public header:
  89. #
  90. # def show
  91. # @article = Article.find(params[:id])
  92. # fresh_when(@article, public: true)
  93. # end
  94. #
  95. # When rendering a different template than the default controller/action
  96. # style, you can indicate which digest to include in the ETag:
  97. #
  98. # before_action { fresh_when @article, template: 'widgets/show' }
  99. #
  100. 1 def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, template: nil)
  101. weak_etag ||= etag || object unless strong_etag
  102. last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
  103. if strong_etag
  104. response.strong_etag = combine_etags strong_etag,
  105. last_modified: last_modified, public: public, template: template
  106. elsif weak_etag || template
  107. response.weak_etag = combine_etags weak_etag,
  108. last_modified: last_modified, public: public, template: template
  109. end
  110. response.last_modified = last_modified if last_modified
  111. response.cache_control[:public] = true if public
  112. head :not_modified if request.fresh?(response)
  113. end
  114. # Sets the +etag+ and/or +last_modified+ on the response and checks it against
  115. # the client request. If the request doesn't match the options provided, the
  116. # request is considered stale and should be generated from scratch. Otherwise,
  117. # it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
  118. #
  119. # === Parameters:
  120. #
  121. # * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
  122. # +:weak_etag+ option.
  123. # * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
  124. # Requests that set If-None-Match header may return a 304 Not Modified
  125. # response if it matches the ETag exactly. A weak ETag indicates semantic
  126. # equivalence, not byte-for-byte equality, so they're good for caching
  127. # HTML pages in browser caches. They can't be used for responses that
  128. # must be byte-identical, like serving Range requests within a PDF file.
  129. # * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
  130. # Requests that set If-None-Match header may return a 304 Not Modified
  131. # response if it matches the ETag exactly. A strong ETag implies exact
  132. # equality: the response must match byte for byte. This is necessary for
  133. # doing Range requests within a large video or PDF file, for example, or
  134. # for compatibility with some CDNs that don't support weak ETags.
  135. # * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
  136. # response. Subsequent requests that set If-Modified-Since may return a
  137. # 304 Not Modified response if last_modified <= If-Modified-Since.
  138. # * <tt>:public</tt> By default the Cache-Control header is private, set this to
  139. # +true+ if you want your application to be cacheable by other devices (proxy caches).
  140. # * <tt>:template</tt> By default, the template digest for the current
  141. # controller/action is included in ETags. If the action renders a
  142. # different template, you can include its digest instead. If the action
  143. # doesn't render a template at all, you can pass <tt>template: false</tt>
  144. # to skip any attempt to check for a template digest.
  145. #
  146. # === Example:
  147. #
  148. # def show
  149. # @article = Article.find(params[:id])
  150. #
  151. # if stale?(etag: @article, last_modified: @article.updated_at)
  152. # @statistics = @article.really_expensive_call
  153. # respond_to do |format|
  154. # # all the supported formats
  155. # end
  156. # end
  157. # end
  158. #
  159. # You can also just pass a record. In this case +last_modified+ will be set
  160. # by calling +updated_at+ and +etag+ by passing the object itself.
  161. #
  162. # def show
  163. # @article = Article.find(params[:id])
  164. #
  165. # if stale?(@article)
  166. # @statistics = @article.really_expensive_call
  167. # respond_to do |format|
  168. # # all the supported formats
  169. # end
  170. # end
  171. # end
  172. #
  173. # You can also pass an object that responds to +maximum+, such as a
  174. # collection of active records. In this case +last_modified+ will be set by
  175. # calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
  176. # most recently updated record) and the +etag+ by passing the object itself.
  177. #
  178. # def index
  179. # @articles = Article.all
  180. #
  181. # if stale?(@articles)
  182. # @statistics = @articles.really_expensive_call
  183. # respond_to do |format|
  184. # # all the supported formats
  185. # end
  186. # end
  187. # end
  188. #
  189. # When passing a record or a collection, you can still set the public header:
  190. #
  191. # def show
  192. # @article = Article.find(params[:id])
  193. #
  194. # if stale?(@article, public: true)
  195. # @statistics = @article.really_expensive_call
  196. # respond_to do |format|
  197. # # all the supported formats
  198. # end
  199. # end
  200. # end
  201. #
  202. # When rendering a different template than the default controller/action
  203. # style, you can indicate which digest to include in the ETag:
  204. #
  205. # def show
  206. # super if stale? @article, template: 'widgets/show'
  207. # end
  208. #
  209. 1 def stale?(object = nil, **freshness_kwargs)
  210. fresh_when(object, **freshness_kwargs)
  211. !request.fresh?(response)
  212. end
  213. # Sets an HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
  214. # instruction, so that intermediate caches must not cache the response.
  215. #
  216. # expires_in 20.minutes
  217. # expires_in 3.hours, public: true
  218. # expires_in 3.hours, public: true, must_revalidate: true
  219. #
  220. # This method will overwrite an existing Cache-Control header.
  221. # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
  222. #
  223. # HTTP Cache-Control Extensions for Stale Content. See https://tools.ietf.org/html/rfc5861
  224. # It helps to cache an asset and serve it while is being revalidated and/or returning with an error.
  225. #
  226. # expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds
  227. # expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds, stale_if_error: 5.minutes
  228. #
  229. # HTTP Cache-Control Extensions other values: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
  230. # Any additional key-value pairs are concatenated onto the `Cache-Control` header in the response:
  231. #
  232. # expires_in 3.hours, public: true, "s-maxage": 3.hours, "no-transform": true
  233. #
  234. # The method will also ensure an HTTP Date header for client compatibility.
  235. 1 def expires_in(seconds, options = {})
  236. response.cache_control.merge!(
  237. max_age: seconds,
  238. public: options.delete(:public),
  239. must_revalidate: options.delete(:must_revalidate),
  240. stale_while_revalidate: options.delete(:stale_while_revalidate),
  241. stale_if_error: options.delete(:stale_if_error),
  242. )
  243. options.delete(:private)
  244. response.cache_control[:extras] = options.map { |k, v| "#{k}=#{v}" }
  245. response.date = Time.now unless response.date?
  246. end
  247. # Sets an HTTP 1.1 Cache-Control header of <tt>no-cache</tt>. This means the
  248. # resource will be marked as stale, so clients must always revalidate.
  249. # Intermediate/browser caches may still store the asset.
  250. 1 def expires_now
  251. response.cache_control.replace(no_cache: true)
  252. end
  253. # Cache or yield the block. The cache is supposed to never expire.
  254. #
  255. # You can use this method when you have an HTTP response that never changes,
  256. # and the browser and proxies should cache it indefinitely.
  257. #
  258. # * +public+: By default, HTTP responses are private, cached only on the
  259. # user's web browser. To allow proxies to cache the response, set +true+ to
  260. # indicate that they can serve the cached response to all users.
  261. 1 def http_cache_forever(public: false)
  262. expires_in 100.years, public: public
  263. yield if stale?(etag: request.fullpath,
  264. last_modified: Time.new(2011, 1, 1).utc,
  265. public: public)
  266. end
  267. 1 private
  268. 1 def combine_etags(validator, options)
  269. [validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
  270. end
  271. end
  272. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/content_security_policy.rb

55.56% lines covered

27 relevant lines. 15 lines covered and 12 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController #:nodoc:
  3. 1 module ContentSecurityPolicy
  4. # TODO: Documentation
  5. 1 extend ActiveSupport::Concern
  6. 1 include AbstractController::Helpers
  7. 1 include AbstractController::Callbacks
  8. 1 included do
  9. 1 helper_method :content_security_policy?
  10. 1 helper_method :content_security_policy_nonce
  11. end
  12. 1 module ClassMethods
  13. 1 def content_security_policy(enabled = true, **options, &block)
  14. before_action(options) do
  15. if block_given?
  16. policy = current_content_security_policy
  17. yield policy
  18. request.content_security_policy = policy
  19. end
  20. unless enabled
  21. request.content_security_policy = nil
  22. end
  23. end
  24. end
  25. 1 def content_security_policy_report_only(report_only = true, **options)
  26. before_action(options) do
  27. request.content_security_policy_report_only = report_only
  28. end
  29. end
  30. end
  31. 1 private
  32. 1 def content_security_policy?
  33. request.content_security_policy
  34. end
  35. 1 def content_security_policy_nonce
  36. request.content_security_policy_nonce
  37. end
  38. 1 def current_content_security_policy
  39. request.content_security_policy&.clone || ActionDispatch::ContentSecurityPolicy.new
  40. end
  41. end
  42. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/cookies.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController #:nodoc:
  3. 1 module Cookies
  4. 1 extend ActiveSupport::Concern
  5. 1 included do
  6. 1 helper_method :cookies if defined?(helper_method)
  7. end
  8. 1 private
  9. # The cookies for the current request. See ActionDispatch::Cookies for
  10. # more information.
  11. 1 def cookies # :doc:
  12. request.cookie_jar
  13. end
  14. end
  15. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/data_streaming.rb

32.43% lines covered

37 relevant lines. 12 lines covered and 25 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_controller/metal/exceptions"
  3. 1 require "action_dispatch/http/content_disposition"
  4. 1 module ActionController #:nodoc:
  5. # Methods for sending arbitrary data and for streaming files to the browser,
  6. # instead of rendering.
  7. 1 module DataStreaming
  8. 1 extend ActiveSupport::Concern
  9. 1 include ActionController::Rendering
  10. 1 DEFAULT_SEND_FILE_TYPE = "application/octet-stream" #:nodoc:
  11. 1 DEFAULT_SEND_FILE_DISPOSITION = "attachment" #:nodoc:
  12. 1 private
  13. # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
  14. # via the Rack::Sendfile middleware. The header to use is set via
  15. # +config.action_dispatch.x_sendfile_header+.
  16. # Your server can also configure this for you by setting the X-Sendfile-Type header.
  17. #
  18. # Be careful to sanitize the path parameter if it is coming from a web
  19. # page. <tt>send_file(params[:path])</tt> allows a malicious user to
  20. # download any file on your server.
  21. #
  22. # Options:
  23. # * <tt>:filename</tt> - suggests a filename for the browser to use.
  24. # Defaults to <tt>File.basename(path)</tt>.
  25. # * <tt>:type</tt> - specifies an HTTP content type.
  26. # You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
  27. # If omitted, the type will be inferred from the file extension specified in <tt>:filename</tt>.
  28. # If no content type is registered for the extension, the default type 'application/octet-stream' will be used.
  29. # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
  30. # Valid values are 'inline' and 'attachment' (default).
  31. # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
  32. # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser to guess the filename from
  33. # the URL, which is necessary for i18n filenames on certain browsers
  34. # (setting <tt>:filename</tt> overrides this option).
  35. #
  36. # The default Content-Type and Content-Disposition headers are
  37. # set to download arbitrary binary files in as many browsers as
  38. # possible. IE versions 4, 5, 5.5, and 6 are all known to have
  39. # a variety of quirks (especially when downloading over SSL).
  40. #
  41. # Simple download:
  42. #
  43. # send_file '/path/to.zip'
  44. #
  45. # Show a JPEG in the browser:
  46. #
  47. # send_file '/path/to.jpeg', type: 'image/jpeg', disposition: 'inline'
  48. #
  49. # Show a 404 page in the browser:
  50. #
  51. # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', disposition: 'inline', status: 404
  52. #
  53. # Read about the other Content-* HTTP headers if you'd like to
  54. # provide the user with more information (such as Content-Description) in
  55. # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
  56. #
  57. # Also be aware that the document may be cached by proxies and browsers.
  58. # The Pragma and Cache-Control headers declare how the file may be cached
  59. # by intermediaries. They default to require clients to validate with
  60. # the server before releasing cached responses. See
  61. # https://www.mnot.net/cache_docs/ for an overview of web caching and
  62. # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
  63. # for the Cache-Control header spec.
  64. 1 def send_file(path, options = {}) #:doc:
  65. raise MissingFile, "Cannot read file #{path}" unless File.file?(path) && File.readable?(path)
  66. options[:filename] ||= File.basename(path) unless options[:url_based_filename]
  67. send_file_headers! options
  68. self.status = options[:status] || 200
  69. self.content_type = options[:content_type] if options.key?(:content_type)
  70. response.send_file path
  71. end
  72. # Sends the given binary data to the browser. This method is similar to
  73. # <tt>render plain: data</tt>, but also allows you to specify whether
  74. # the browser should display the response as a file attachment (i.e. in a
  75. # download dialog) or as inline data. You may also set the content type,
  76. # the file name, and other things.
  77. #
  78. # Options:
  79. # * <tt>:filename</tt> - suggests a filename for the browser to use.
  80. # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
  81. # You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
  82. # If omitted, type will be inferred from the file extension specified in <tt>:filename</tt>.
  83. # If no content type is registered for the extension, the default type 'application/octet-stream' will be used.
  84. # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
  85. # Valid values are 'inline' and 'attachment' (default).
  86. # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
  87. #
  88. # Generic data download:
  89. #
  90. # send_data buffer
  91. #
  92. # Download a dynamically-generated tarball:
  93. #
  94. # send_data generate_tgz('dir'), filename: 'dir.tgz'
  95. #
  96. # Display an image Active Record in the browser:
  97. #
  98. # send_data image.data, type: image.content_type, disposition: 'inline'
  99. #
  100. # See +send_file+ for more information on HTTP Content-* headers and caching.
  101. 1 def send_data(data, options = {}) #:doc:
  102. send_file_headers! options
  103. render options.slice(:status, :content_type).merge(body: data)
  104. end
  105. 1 def send_file_headers!(options)
  106. type_provided = options.has_key?(:type)
  107. content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
  108. self.content_type = content_type
  109. response.sending_file = true
  110. raise ArgumentError, ":type option required" if content_type.nil?
  111. if content_type.is_a?(Symbol)
  112. extension = Mime[content_type]
  113. raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
  114. self.content_type = extension
  115. else
  116. if !type_provided && options[:filename]
  117. # If type wasn't provided, try guessing from file extension.
  118. content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete(".")) || content_type
  119. end
  120. self.content_type = content_type
  121. end
  122. disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
  123. if disposition
  124. headers["Content-Disposition"] = ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: options[:filename])
  125. end
  126. headers["Content-Transfer-Encoding"] = "binary"
  127. # Fix a problem with IE 6.0 on opening downloaded files:
  128. # If Cache-Control: no-cache is set (which Rails does by default),
  129. # IE removes the file it just downloaded from its cache immediately
  130. # after it displays the "open/save" dialog, which means that if you
  131. # hit "open" the file isn't there anymore when the application that
  132. # is called for handling the download is run, so let's workaround that
  133. response.cache_control[:public] ||= false
  134. end
  135. end
  136. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/default_headers.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # Allows configuring default headers that will be automatically merged into
  4. # each response.
  5. 1 module DefaultHeaders
  6. 1 extend ActiveSupport::Concern
  7. 1 module ClassMethods
  8. 1 def make_response!(request)
  9. 1 ActionDispatch::Response.create.tap do |res|
  10. 1 res.request = request
  11. end
  12. end
  13. end
  14. end
  15. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/etag_with_flash.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # When you're using the flash, it's generally used as a conditional on the view.
  4. # This means the content of the view depends on the flash. Which in turn means
  5. # that the ETag for a response should be computed with the content of the flash
  6. # in mind. This does that by including the content of the flash as a component
  7. # in the ETag that's generated for a response.
  8. 1 module EtagWithFlash
  9. 1 extend ActiveSupport::Concern
  10. 1 include ActionController::ConditionalGet
  11. 1 included do
  12. 1 etag { flash unless flash.empty? }
  13. end
  14. end
  15. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/etag_with_template_digest.rb

64.71% lines covered

17 relevant lines. 11 lines covered and 6 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # When our views change, they should bubble up into HTTP cache freshness
  4. # and bust browser caches. So the template digest for the current action
  5. # is automatically included in the ETag.
  6. #
  7. # Enabled by default for apps that use Action View. Disable by setting
  8. #
  9. # config.action_controller.etag_with_template_digest = false
  10. #
  11. # Override the template to digest by passing +:template+ to +fresh_when+
  12. # and +stale?+ calls. For example:
  13. #
  14. # # We're going to render widgets/show, not posts/show
  15. # fresh_when @post, template: 'widgets/show'
  16. #
  17. # # We're not going to render a template, so omit it from the ETag.
  18. # fresh_when @post, template: false
  19. #
  20. 1 module EtagWithTemplateDigest
  21. 1 extend ActiveSupport::Concern
  22. 1 include ActionController::ConditionalGet
  23. 1 included do
  24. 1 class_attribute :etag_with_template_digest, default: true
  25. 1 etag do |options|
  26. determine_template_etag(options) if etag_with_template_digest
  27. end
  28. end
  29. 1 private
  30. 1 def determine_template_etag(options)
  31. if template = pick_template_for_etag(options)
  32. lookup_and_digest_template(template)
  33. end
  34. end
  35. # Pick the template digest to include in the ETag. If the +:template+ option
  36. # is present, use the named template. If +:template+ is +nil+ or absent, use
  37. # the default controller/action template. If +:template+ is false, omit the
  38. # template digest from the ETag.
  39. 1 def pick_template_for_etag(options)
  40. unless options[:template] == false
  41. options[:template] || "#{controller_path}/#{action_name}"
  42. end
  43. end
  44. 1 def lookup_and_digest_template(template)
  45. ActionView::Digestor.digest name: template, format: nil, finder: lookup_context
  46. end
  47. end
  48. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/flash.rb

69.57% lines covered

23 relevant lines. 16 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController #:nodoc:
  3. 1 module Flash
  4. 1 extend ActiveSupport::Concern
  5. 1 included do
  6. 1 class_attribute :_flash_types, instance_accessor: false, default: []
  7. 1 delegate :flash, to: :request
  8. 1 add_flash_types(:alert, :notice)
  9. end
  10. 1 module ClassMethods
  11. # Creates new flash types. You can pass as many types as you want to create
  12. # flash types other than the default <tt>alert</tt> and <tt>notice</tt> in
  13. # your controllers and views. For instance:
  14. #
  15. # # in application_controller.rb
  16. # class ApplicationController < ActionController::Base
  17. # add_flash_types :warning
  18. # end
  19. #
  20. # # in your controller
  21. # redirect_to user_path(@user), warning: "Incomplete profile"
  22. #
  23. # # in your view
  24. # <%= warning %>
  25. #
  26. # This method will automatically define a new method for each of the given
  27. # names, and it will be available in your views.
  28. 1 def add_flash_types(*types)
  29. 1 types.each do |type|
  30. 2 next if _flash_types.include?(type)
  31. 2 define_method(type) do
  32. request.flash[type]
  33. end
  34. 2 helper_method(type) if respond_to?(:helper_method)
  35. 2 self._flash_types += [type]
  36. end
  37. end
  38. end
  39. 1 private
  40. 1 def redirect_to(options = {}, response_options_and_flash = {}) #:doc:
  41. self.class._flash_types.each do |flash_type|
  42. if type = response_options_and_flash.delete(flash_type)
  43. flash[flash_type] = type
  44. end
  45. end
  46. if other_flashes = response_options_and_flash.delete(:flash)
  47. flash.update(other_flashes)
  48. end
  49. super(options, response_options_and_flash)
  50. end
  51. end
  52. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/head.rb

20.83% lines covered

24 relevant lines. 5 lines covered and 19 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 module Head
  4. # Returns a response that has no content (merely headers). The options
  5. # argument is interpreted to be a hash of header names and values.
  6. # This allows you to easily return a response that consists only of
  7. # significant headers:
  8. #
  9. # head :created, location: person_path(@person)
  10. #
  11. # head :created, location: @person
  12. #
  13. # It can also be used to return exceptional conditions:
  14. #
  15. # return head(:method_not_allowed) unless request.post?
  16. # return head(:bad_request) unless valid_request?
  17. # render
  18. #
  19. # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list of valid +status+ symbols.
  20. 1 def head(status, options = {})
  21. if status.is_a?(Hash)
  22. raise ArgumentError, "#{status.inspect} is not a valid value for `status`."
  23. end
  24. status ||= :ok
  25. location = options.delete(:location)
  26. content_type = options.delete(:content_type)
  27. options.each do |key, value|
  28. headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
  29. end
  30. self.status = status
  31. self.location = url_for(location) if location
  32. if include_content?(response_code)
  33. unless self.media_type
  34. self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
  35. end
  36. response.charset = false
  37. end
  38. self.response_body = ""
  39. true
  40. end
  41. 1 private
  42. 1 def include_content?(status)
  43. case status
  44. when 100..199
  45. false
  46. when 204, 205, 304
  47. false
  48. else
  49. true
  50. end
  51. end
  52. end
  53. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/http_authentication.rb

40.0% lines covered

145 relevant lines. 58 lines covered and 87 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "base64"
  3. 1 require "active_support/security_utils"
  4. 1 module ActionController
  5. # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
  6. 1 module HttpAuthentication
  7. # Makes it dead easy to do HTTP \Basic authentication.
  8. #
  9. # === Simple \Basic example
  10. #
  11. # class PostsController < ApplicationController
  12. # http_basic_authenticate_with name: "dhh", password: "secret", except: :index
  13. #
  14. # def index
  15. # render plain: "Everyone can see me!"
  16. # end
  17. #
  18. # def edit
  19. # render plain: "I'm only accessible if you know the password"
  20. # end
  21. # end
  22. #
  23. # === Advanced \Basic example
  24. #
  25. # Here is a more advanced \Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
  26. # the regular HTML interface is protected by a session approach:
  27. #
  28. # class ApplicationController < ActionController::Base
  29. # before_action :set_account, :authenticate
  30. #
  31. # private
  32. # def set_account
  33. # @account = Account.find_by(url_name: request.subdomains.first)
  34. # end
  35. #
  36. # def authenticate
  37. # case request.format
  38. # when Mime[:xml], Mime[:atom]
  39. # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
  40. # @current_user = user
  41. # else
  42. # request_http_basic_authentication
  43. # end
  44. # else
  45. # if session_authenticated?
  46. # @current_user = @account.users.find(session[:authenticated][:user_id])
  47. # else
  48. # redirect_to(login_url) and return false
  49. # end
  50. # end
  51. # end
  52. # end
  53. #
  54. # In your integration tests, you can do something like this:
  55. #
  56. # def test_access_granted_from_xml
  57. # authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
  58. #
  59. # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
  60. #
  61. # assert_equal 200, status
  62. # end
  63. 1 module Basic
  64. 1 extend self
  65. 1 module ControllerMethods
  66. 1 extend ActiveSupport::Concern
  67. 1 module ClassMethods
  68. 1 def http_basic_authenticate_with(name:, password:, realm: nil, **options)
  69. before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
  70. end
  71. end
  72. 1 def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
  73. authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
  74. # This comparison uses & so that it doesn't short circuit and
  75. # uses `secure_compare` so that length information isn't leaked.
  76. ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
  77. ActiveSupport::SecurityUtils.secure_compare(given_password, password)
  78. end
  79. end
  80. 1 def authenticate_or_request_with_http_basic(realm = nil, message = nil, &login_procedure)
  81. authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm || "Application", message)
  82. end
  83. 1 def authenticate_with_http_basic(&login_procedure)
  84. HttpAuthentication::Basic.authenticate(request, &login_procedure)
  85. end
  86. 1 def request_http_basic_authentication(realm = "Application", message = nil)
  87. HttpAuthentication::Basic.authentication_request(self, realm, message)
  88. end
  89. end
  90. 1 def authenticate(request, &login_procedure)
  91. if has_basic_credentials?(request)
  92. login_procedure.call(*user_name_and_password(request))
  93. end
  94. end
  95. 1 def has_basic_credentials?(request)
  96. request.authorization.present? && (auth_scheme(request).downcase == "basic")
  97. end
  98. 1 def user_name_and_password(request)
  99. decode_credentials(request).split(":", 2)
  100. end
  101. 1 def decode_credentials(request)
  102. ::Base64.decode64(auth_param(request) || "")
  103. end
  104. 1 def auth_scheme(request)
  105. request.authorization.to_s.split(" ", 2).first
  106. end
  107. 1 def auth_param(request)
  108. request.authorization.to_s.split(" ", 2).second
  109. end
  110. 1 def encode_credentials(user_name, password)
  111. "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
  112. end
  113. 1 def authentication_request(controller, realm, message)
  114. message ||= "HTTP Basic: Access denied.\n"
  115. controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"', "")}")
  116. controller.status = 401
  117. controller.response_body = message
  118. end
  119. end
  120. # Makes it dead easy to do HTTP \Digest authentication.
  121. #
  122. # === Simple \Digest example
  123. #
  124. # require "digest/md5"
  125. # class PostsController < ApplicationController
  126. # REALM = "SuperSecret"
  127. # USERS = {"dhh" => "secret", #plain text password
  128. # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
  129. #
  130. # before_action :authenticate, except: [:index]
  131. #
  132. # def index
  133. # render plain: "Everyone can see me!"
  134. # end
  135. #
  136. # def edit
  137. # render plain: "I'm only accessible if you know the password"
  138. # end
  139. #
  140. # private
  141. # def authenticate
  142. # authenticate_or_request_with_http_digest(REALM) do |username|
  143. # USERS[username]
  144. # end
  145. # end
  146. # end
  147. #
  148. # === Notes
  149. #
  150. # The +authenticate_or_request_with_http_digest+ block must return the user's password
  151. # or the ha1 digest hash so the framework can appropriately hash to check the user's
  152. # credentials. Returning +nil+ will cause authentication to fail.
  153. #
  154. # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
  155. # the password file or database is compromised, the attacker would be able to use the ha1 hash to
  156. # authenticate as the user at this +realm+, but would not have the user's password to try using at
  157. # other sites.
  158. #
  159. # In rare instances, web servers or front proxies strip authorization headers before
  160. # they reach your application. You can debug this situation by logging all environment
  161. # variables, and check for HTTP_AUTHORIZATION, amongst others.
  162. 1 module Digest
  163. 1 extend self
  164. 1 module ControllerMethods
  165. 1 def authenticate_or_request_with_http_digest(realm = "Application", message = nil, &password_procedure)
  166. authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm, message)
  167. end
  168. # Authenticate with HTTP Digest, returns true or false
  169. 1 def authenticate_with_http_digest(realm = "Application", &password_procedure)
  170. HttpAuthentication::Digest.authenticate(request, realm, &password_procedure)
  171. end
  172. # Render output including the HTTP Digest authentication header
  173. 1 def request_http_digest_authentication(realm = "Application", message = nil)
  174. HttpAuthentication::Digest.authentication_request(self, realm, message)
  175. end
  176. end
  177. # Returns false on a valid response, true otherwise
  178. 1 def authenticate(request, realm, &password_procedure)
  179. request.authorization && validate_digest_response(request, realm, &password_procedure)
  180. end
  181. # Returns false unless the request credentials response value matches the expected value.
  182. # First try the password as a ha1 digest password. If this fails, then try it as a plain
  183. # text password.
  184. 1 def validate_digest_response(request, realm, &password_procedure)
  185. secret_key = secret_token(request)
  186. credentials = decode_credentials_header(request)
  187. valid_nonce = validate_nonce(secret_key, request, credentials[:nonce])
  188. if valid_nonce && realm == credentials[:realm] && opaque(secret_key) == credentials[:opaque]
  189. password = password_procedure.call(credentials[:username])
  190. return false unless password
  191. method = request.get_header("rack.methodoverride.original_method") || request.get_header("REQUEST_METHOD")
  192. uri = credentials[:uri]
  193. [true, false].any? do |trailing_question_mark|
  194. [true, false].any? do |password_is_ha1|
  195. _uri = trailing_question_mark ? uri + "?" : uri
  196. expected = expected_response(method, _uri, credentials, password, password_is_ha1)
  197. expected == credentials[:response]
  198. end
  199. end
  200. end
  201. end
  202. # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
  203. # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
  204. # of a plain-text password.
  205. 1 def expected_response(http_method, uri, credentials, password, password_is_ha1 = true)
  206. ha1 = password_is_ha1 ? password : ha1(credentials, password)
  207. ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
  208. ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
  209. end
  210. 1 def ha1(credentials, password)
  211. ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
  212. end
  213. 1 def encode_credentials(http_method, credentials, password, password_is_ha1)
  214. credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
  215. "Digest " + credentials.sort_by { |x| x[0].to_s }.map { |v| "#{v[0]}='#{v[1]}'" }.join(", ")
  216. end
  217. 1 def decode_credentials_header(request)
  218. decode_credentials(request.authorization)
  219. end
  220. 1 def decode_credentials(header)
  221. ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, "").split(",").map do |pair|
  222. key, value = pair.split("=", 2)
  223. [key.strip, value.to_s.gsub(/^"|"$/, "").delete("'")]
  224. end]
  225. end
  226. 1 def authentication_header(controller, realm)
  227. secret_key = secret_token(controller.request)
  228. nonce = self.nonce(secret_key)
  229. opaque = opaque(secret_key)
  230. controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
  231. end
  232. 1 def authentication_request(controller, realm, message = nil)
  233. message ||= "HTTP Digest: Access denied.\n"
  234. authentication_header(controller, realm)
  235. controller.status = 401
  236. controller.response_body = message
  237. end
  238. 1 def secret_token(request)
  239. key_generator = request.key_generator
  240. http_auth_salt = request.http_auth_salt
  241. key_generator.generate_key(http_auth_salt)
  242. end
  243. # Uses an MD5 digest based on time to generate a value to be used only once.
  244. #
  245. # A server-specified data string which should be uniquely generated each time a 401 response is made.
  246. # It is recommended that this string be base64 or hexadecimal data.
  247. # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed.
  248. #
  249. # The contents of the nonce are implementation dependent.
  250. # The quality of the implementation depends on a good choice.
  251. # A nonce might, for example, be constructed as the base 64 encoding of
  252. #
  253. # time-stamp H(time-stamp ":" ETag ":" private-key)
  254. #
  255. # where time-stamp is a server-generated time or other non-repeating value,
  256. # ETag is the value of the HTTP ETag header associated with the requested entity,
  257. # and private-key is data known only to the server.
  258. # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and
  259. # reject the request if it did not match the nonce from that header or
  260. # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity.
  261. # The inclusion of the ETag prevents a replay request for an updated version of the resource.
  262. # (Note: including the IP address of the client in the nonce would appear to offer the server the ability
  263. # to limit the reuse of the nonce to the same client that originally got it.
  264. # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm.
  265. # Also, IP address spoofing is not that hard.)
  266. #
  267. # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
  268. # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
  269. # POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
  270. # of this document.
  271. #
  272. # The nonce is opaque to the client. Composed of Time, and hash of Time with secret
  273. # key from the Rails session secret generated upon creation of project. Ensures
  274. # the time cannot be modified by client.
  275. 1 def nonce(secret_key, time = Time.now)
  276. t = time.to_i
  277. hashed = [t, secret_key]
  278. digest = ::Digest::MD5.hexdigest(hashed.join(":"))
  279. ::Base64.strict_encode64("#{t}:#{digest}")
  280. end
  281. # Might want a shorter timeout depending on whether the request
  282. # is a PATCH, PUT, or POST, and if the client is a browser or web service.
  283. # Can be much shorter if the Stale directive is implemented. This would
  284. # allow a user to use new nonce without prompting the user again for their
  285. # username and password.
  286. 1 def validate_nonce(secret_key, request, value, seconds_to_timeout = 5 * 60)
  287. return false if value.nil?
  288. t = ::Base64.decode64(value).split(":").first.to_i
  289. nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
  290. end
  291. # Opaque based on digest of secret key
  292. 1 def opaque(secret_key)
  293. ::Digest::MD5.hexdigest(secret_key)
  294. end
  295. end
  296. # Makes it dead easy to do HTTP Token authentication.
  297. #
  298. # Simple Token example:
  299. #
  300. # class PostsController < ApplicationController
  301. # TOKEN = "secret"
  302. #
  303. # before_action :authenticate, except: [ :index ]
  304. #
  305. # def index
  306. # render plain: "Everyone can see me!"
  307. # end
  308. #
  309. # def edit
  310. # render plain: "I'm only accessible if you know the password"
  311. # end
  312. #
  313. # private
  314. # def authenticate
  315. # authenticate_or_request_with_http_token do |token, options|
  316. # # Compare the tokens in a time-constant manner, to mitigate
  317. # # timing attacks.
  318. # ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
  319. # end
  320. # end
  321. # end
  322. #
  323. #
  324. # Here is a more advanced Token example where only Atom feeds and the XML API is protected by HTTP token authentication,
  325. # the regular HTML interface is protected by a session approach:
  326. #
  327. # class ApplicationController < ActionController::Base
  328. # before_action :set_account, :authenticate
  329. #
  330. # private
  331. # def set_account
  332. # @account = Account.find_by(url_name: request.subdomains.first)
  333. # end
  334. #
  335. # def authenticate
  336. # case request.format
  337. # when Mime[:xml], Mime[:atom]
  338. # if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
  339. # @current_user = user
  340. # else
  341. # request_http_token_authentication
  342. # end
  343. # else
  344. # if session_authenticated?
  345. # @current_user = @account.users.find(session[:authenticated][:user_id])
  346. # else
  347. # redirect_to(login_url) and return false
  348. # end
  349. # end
  350. # end
  351. # end
  352. #
  353. #
  354. # In your integration tests, you can do something like this:
  355. #
  356. # def test_access_granted_from_xml
  357. # authorization = ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
  358. #
  359. # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
  360. #
  361. # assert_equal 200, status
  362. # end
  363. #
  364. #
  365. # On shared hosts, Apache sometimes doesn't pass authentication headers to
  366. # FCGI instances. If your environment matches this description and you cannot
  367. # authenticate, try this rule in your Apache setup:
  368. #
  369. # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
  370. 1 module Token
  371. 1 TOKEN_KEY = "token="
  372. 1 TOKEN_REGEX = /^(Token|Bearer)\s+/
  373. 1 AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
  374. 1 extend self
  375. 1 module ControllerMethods
  376. 1 def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure)
  377. authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message)
  378. end
  379. 1 def authenticate_with_http_token(&login_procedure)
  380. Token.authenticate(self, &login_procedure)
  381. end
  382. 1 def request_http_token_authentication(realm = "Application", message = nil)
  383. Token.authentication_request(self, realm, message)
  384. end
  385. end
  386. # If token Authorization header is present, call the login
  387. # procedure with the present token and options.
  388. #
  389. # [controller]
  390. # ActionController::Base instance for the current request.
  391. #
  392. # [login_procedure]
  393. # Proc to call if a token is present. The Proc should take two arguments:
  394. #
  395. # authenticate(controller) { |token, options| ... }
  396. #
  397. # Returns the return value of <tt>login_procedure</tt> if a
  398. # token is found. Returns <tt>nil</tt> if no token is found.
  399. 1 def authenticate(controller, &login_procedure)
  400. token, options = token_and_options(controller.request)
  401. unless token.blank?
  402. login_procedure.call(token, options)
  403. end
  404. end
  405. # Parses the token and options out of the token Authorization header.
  406. # The value for the Authorization header is expected to have the prefix
  407. # <tt>"Token"</tt> or <tt>"Bearer"</tt>. If the header looks like this:
  408. # Authorization: Token token="abc", nonce="def"
  409. # Then the returned token is <tt>"abc"</tt>, and the options are
  410. # <tt>{nonce: "def"}</tt>
  411. #
  412. # request - ActionDispatch::Request instance with the current headers.
  413. #
  414. # Returns an +Array+ of <tt>[String, Hash]</tt> if a token is present.
  415. # Returns +nil+ if no token is found.
  416. 1 def token_and_options(request)
  417. authorization_request = request.authorization.to_s
  418. if authorization_request[TOKEN_REGEX]
  419. params = token_params_from authorization_request
  420. [params.shift[1], Hash[params].with_indifferent_access]
  421. end
  422. end
  423. 1 def token_params_from(auth)
  424. rewrite_param_values params_array_from raw_params auth
  425. end
  426. # Takes raw_params and turns it into an array of parameters
  427. 1 def params_array_from(raw_params)
  428. raw_params.map { |param| param.split %r/=(.+)?/ }
  429. end
  430. # This removes the <tt>"</tt> characters wrapping the value.
  431. 1 def rewrite_param_values(array_params)
  432. array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
  433. end
  434. # This method takes an authorization body and splits up the key-value
  435. # pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt>
  436. # delimiters defined in +AUTHN_PAIR_DELIMITERS+.
  437. 1 def raw_params(auth)
  438. _raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
  439. if !_raw_params.first&.start_with?(TOKEN_KEY)
  440. _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
  441. end
  442. _raw_params
  443. end
  444. # Encodes the given token and options into an Authorization header value.
  445. #
  446. # token - String token.
  447. # options - optional Hash of the options.
  448. #
  449. # Returns String.
  450. 1 def encode_credentials(token, options = {})
  451. values = ["#{TOKEN_KEY}#{token.to_s.inspect}"] + options.map do |key, value|
  452. "#{key}=#{value.to_s.inspect}"
  453. end
  454. "Token #{values * ", "}"
  455. end
  456. # Sets a WWW-Authenticate header to let the client know a token is desired.
  457. #
  458. # controller - ActionController::Base instance for the outgoing response.
  459. # realm - String realm to use in the header.
  460. #
  461. # Returns nothing.
  462. 1 def authentication_request(controller, realm, message = nil)
  463. message ||= "HTTP Token: Access denied.\n"
  464. controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
  465. controller.__send__ :render, plain: message, status: :unauthorized
  466. end
  467. end
  468. end
  469. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/implicit_render.rb

35.0% lines covered

20 relevant lines. 7 lines covered and 13 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # Handles implicit rendering for a controller action that does not
  4. # explicitly respond with +render+, +respond_to+, +redirect+, or +head+.
  5. #
  6. # For API controllers, the implicit response is always <tt>204 No Content</tt>.
  7. #
  8. # For all other controllers, we use these heuristics to decide whether to
  9. # render a template, raise an error for a missing template, or respond with
  10. # <tt>204 No Content</tt>:
  11. #
  12. # First, if we DO find a template, it's rendered. Template lookup accounts
  13. # for the action name, locales, format, variant, template handlers, and more
  14. # (see +render+ for details).
  15. #
  16. # Second, if we DON'T find a template but the controller action does have
  17. # templates for other formats, variants, etc., then we trust that you meant
  18. # to provide a template for this response, too, and we raise
  19. # <tt>ActionController::UnknownFormat</tt> with an explanation.
  20. #
  21. # Third, if we DON'T find a template AND the request is a page load in a web
  22. # browser (technically, a non-XHR GET request for an HTML response) where
  23. # you reasonably expect to have rendered a template, then we raise
  24. # <tt>ActionController::MissingExactTemplate</tt> with an explanation.
  25. #
  26. # Finally, if we DON'T find a template AND the request isn't a browser page
  27. # load, then we implicitly respond with <tt>204 No Content</tt>.
  28. 1 module ImplicitRender
  29. # :stopdoc:
  30. 1 include BasicImplicitRender
  31. 1 def default_render
  32. if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
  33. render
  34. elsif any_templates?(action_name.to_s, _prefixes)
  35. message = "#{self.class.name}\##{action_name} is missing a template " \
  36. "for this request format and variant.\n" \
  37. "\nrequest.formats: #{request.formats.map(&:to_s).inspect}" \
  38. "\nrequest.variant: #{request.variant.inspect}"
  39. raise ActionController::UnknownFormat, message
  40. elsif interactive_browser_request?
  41. message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
  42. raise ActionController::MissingExactTemplate, message
  43. else
  44. logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
  45. super
  46. end
  47. end
  48. 1 def method_for_action(action_name)
  49. super || if template_exists?(action_name.to_s, _prefixes)
  50. "default_render"
  51. end
  52. end
  53. 1 private
  54. 1 def interactive_browser_request?
  55. request.get? && request.format == Mime[:html] && !request.xhr?
  56. end
  57. end
  58. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/instrumentation.rb

77.78% lines covered

45 relevant lines. 35 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "benchmark"
  3. 1 require "abstract_controller/logger"
  4. 1 module ActionController
  5. # Adds instrumentation to several ends in ActionController::Base. It also provides
  6. # some hooks related with process_action. This allows an ORM like Active Record
  7. # and/or DataMapper to plug in ActionController and show related information.
  8. #
  9. # Check ActiveRecord::Railties::ControllerRuntime for an example.
  10. 1 module Instrumentation
  11. 1 extend ActiveSupport::Concern
  12. 1 include AbstractController::Logger
  13. 1 attr_internal :view_runtime
  14. 1 def process_action(*)
  15. raw_payload = {
  16. 1 controller: self.class.name,
  17. action: action_name,
  18. request: request,
  19. params: request.filtered_parameters,
  20. headers: request.headers,
  21. format: request.format.ref,
  22. method: request.request_method,
  23. path: request.fullpath
  24. }
  25. 1 ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
  26. 1 ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
  27. 1 result = super
  28. 1 payload[:response] = response
  29. 1 payload[:status] = response.status
  30. 1 result
  31. ensure
  32. 1 append_info_to_payload(payload)
  33. end
  34. end
  35. 1 def render(*)
  36. 1 render_output = nil
  37. 1 self.view_runtime = cleanup_view_runtime do
  38. 2 Benchmark.ms { render_output = super }
  39. end
  40. 1 render_output
  41. end
  42. 1 def send_file(path, options = {})
  43. ActiveSupport::Notifications.instrument("send_file.action_controller",
  44. options.merge(path: path)) do
  45. super
  46. end
  47. end
  48. 1 def send_data(data, options = {})
  49. ActiveSupport::Notifications.instrument("send_data.action_controller", options) do
  50. super
  51. end
  52. end
  53. 1 def redirect_to(*)
  54. ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload|
  55. result = super
  56. payload[:status] = response.status
  57. payload[:location] = response.filtered_location
  58. result
  59. end
  60. end
  61. 1 private
  62. # A hook invoked every time a before callback is halted.
  63. 1 def halted_callback_hook(filter, _)
  64. ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
  65. end
  66. # A hook which allows you to clean up any time, wrongly taken into account in
  67. # views, like database querying time.
  68. #
  69. # def cleanup_view_runtime
  70. # super - time_taken_in_something_expensive
  71. # end
  72. 1 def cleanup_view_runtime # :doc:
  73. 1 yield
  74. end
  75. # Every time after an action is processed, this method is invoked
  76. # with the payload, so you can add more information.
  77. 1 def append_info_to_payload(payload) # :doc:
  78. 1 payload[:view_runtime] = view_runtime
  79. end
  80. 1 module ClassMethods
  81. # A hook which allows other frameworks to log what happened during
  82. # controller process action. This method should return an array
  83. # with the messages to be added.
  84. 1 def log_process_action(payload) #:nodoc:
  85. 1 messages, view_runtime = [], payload[:view_runtime]
  86. 1 messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
  87. 1 messages
  88. end
  89. end
  90. end
  91. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/live.rb

30.83% lines covered

120 relevant lines. 37 lines covered and 83 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_dispatch/http/response"
  3. 1 require "delegate"
  4. 1 require "active_support/json"
  5. 1 module ActionController
  6. # Mix this module into your controller, and all actions in that controller
  7. # will be able to stream data to the client as it's written.
  8. #
  9. # class MyController < ActionController::Base
  10. # include ActionController::Live
  11. #
  12. # def stream
  13. # response.headers['Content-Type'] = 'text/event-stream'
  14. # 100.times {
  15. # response.stream.write "hello world\n"
  16. # sleep 1
  17. # }
  18. # ensure
  19. # response.stream.close
  20. # end
  21. # end
  22. #
  23. # There are a few caveats with this module. You *cannot* write headers after the
  24. # response has been committed (Response#committed? will return truthy).
  25. # Calling +write+ or +close+ on the response stream will cause the response
  26. # object to be committed. Make sure all headers are set before calling write
  27. # or close on your stream.
  28. #
  29. # You *must* call close on your stream when you're finished, otherwise the
  30. # socket may be left open forever.
  31. #
  32. # The final caveat is that your actions are executed in a separate thread than
  33. # the main thread. Make sure your actions are thread safe, and this shouldn't
  34. # be a problem (don't share state across threads, etc).
  35. 1 module Live
  36. 1 extend ActiveSupport::Concern
  37. 1 module ClassMethods
  38. 1 def make_response!(request)
  39. if request.get_header("HTTP_VERSION") == "HTTP/1.0"
  40. super
  41. else
  42. Live::Response.new.tap do |res|
  43. res.request = request
  44. end
  45. end
  46. end
  47. end
  48. # This class provides the ability to write an SSE (Server Sent Event)
  49. # to an IO stream. The class is initialized with a stream and can be used
  50. # to either write a JSON string or an object which can be converted to JSON.
  51. #
  52. # Writing an object will convert it into standard SSE format with whatever
  53. # options you have configured. You may choose to set the following options:
  54. #
  55. # 1) Event. If specified, an event with this name will be dispatched on
  56. # the browser.
  57. # 2) Retry. The reconnection time in milliseconds used when attempting
  58. # to send the event.
  59. # 3) Id. If the connection dies while sending an SSE to the browser, then
  60. # the server will receive a +Last-Event-ID+ header with value equal to +id+.
  61. #
  62. # After setting an option in the constructor of the SSE object, all future
  63. # SSEs sent across the stream will use those options unless overridden.
  64. #
  65. # Example Usage:
  66. #
  67. # class MyController < ActionController::Base
  68. # include ActionController::Live
  69. #
  70. # def index
  71. # response.headers['Content-Type'] = 'text/event-stream'
  72. # sse = SSE.new(response.stream, retry: 300, event: "event-name")
  73. # sse.write({ name: 'John'})
  74. # sse.write({ name: 'John'}, id: 10)
  75. # sse.write({ name: 'John'}, id: 10, event: "other-event")
  76. # sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500)
  77. # ensure
  78. # sse.close
  79. # end
  80. # end
  81. #
  82. # Note: SSEs are not currently supported by IE. However, they are supported
  83. # by Chrome, Firefox, Opera, and Safari.
  84. 1 class SSE
  85. 1 PERMITTED_OPTIONS = %w( retry event id )
  86. 1 def initialize(stream, options = {})
  87. @stream = stream
  88. @options = options
  89. end
  90. 1 def close
  91. @stream.close
  92. end
  93. 1 def write(object, options = {})
  94. case object
  95. when String
  96. perform_write(object, options)
  97. else
  98. perform_write(ActiveSupport::JSON.encode(object), options)
  99. end
  100. end
  101. 1 private
  102. 1 def perform_write(json, options)
  103. current_options = @options.merge(options).stringify_keys
  104. PERMITTED_OPTIONS.each do |option_name|
  105. if (option_value = current_options[option_name])
  106. @stream.write "#{option_name}: #{option_value}\n"
  107. end
  108. end
  109. message = json.gsub("\n", "\ndata: ")
  110. @stream.write "data: #{message}\n\n"
  111. end
  112. end
  113. 1 class ClientDisconnected < RuntimeError
  114. end
  115. 1 class Buffer < ActionDispatch::Response::Buffer #:nodoc:
  116. 1 include MonitorMixin
  117. # Ignore that the client has disconnected.
  118. #
  119. # If this value is `true`, calling `write` after the client
  120. # disconnects will result in the written content being silently
  121. # discarded. If this value is `false` (the default), a
  122. # ClientDisconnected exception will be raised.
  123. 1 attr_accessor :ignore_disconnect
  124. 1 def initialize(response)
  125. super(response, SizedQueue.new(10))
  126. @error_callback = lambda { true }
  127. @cv = new_cond
  128. @aborted = false
  129. @ignore_disconnect = false
  130. end
  131. 1 def write(string)
  132. unless @response.committed?
  133. @response.headers["Cache-Control"] ||= "no-cache"
  134. @response.delete_header "Content-Length"
  135. end
  136. super
  137. unless connected?
  138. @buf.clear
  139. unless @ignore_disconnect
  140. # Raise ClientDisconnected, which is a RuntimeError (not an
  141. # IOError), because that's more appropriate for something beyond
  142. # the developer's control.
  143. raise ClientDisconnected, "client disconnected"
  144. end
  145. end
  146. end
  147. # Write a 'close' event to the buffer; the producer/writing thread
  148. # uses this to notify us that it's finished supplying content.
  149. #
  150. # See also #abort.
  151. 1 def close
  152. synchronize do
  153. super
  154. @buf.push nil
  155. @cv.broadcast
  156. end
  157. end
  158. # Inform the producer/writing thread that the client has
  159. # disconnected; the reading thread is no longer interested in
  160. # anything that's being written.
  161. #
  162. # See also #close.
  163. 1 def abort
  164. synchronize do
  165. @aborted = true
  166. @buf.clear
  167. end
  168. end
  169. # Is the client still connected and waiting for content?
  170. #
  171. # The result of calling `write` when this is `false` is determined
  172. # by `ignore_disconnect`.
  173. 1 def connected?
  174. !@aborted
  175. end
  176. 1 def on_error(&block)
  177. @error_callback = block
  178. end
  179. 1 def call_on_error
  180. @error_callback.call
  181. end
  182. 1 private
  183. 1 def each_chunk(&block)
  184. loop do
  185. str = nil
  186. ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
  187. str = @buf.pop
  188. end
  189. break unless str
  190. yield str
  191. end
  192. end
  193. end
  194. 1 class Response < ActionDispatch::Response #:nodoc: all
  195. 1 private
  196. 1 def before_committed
  197. super
  198. jar = request.cookie_jar
  199. # The response can be committed multiple times
  200. jar.write self unless committed?
  201. end
  202. 1 def build_buffer(response, body)
  203. buf = Live::Buffer.new response
  204. body.each { |part| buf.write part }
  205. buf
  206. end
  207. end
  208. 1 def process(name)
  209. t1 = Thread.current
  210. locals = t1.keys.map { |key| [key, t1[key]] }
  211. error = nil
  212. # This processes the action in a child thread. It lets us return the
  213. # response code and headers back up the Rack stack, and still process
  214. # the body in parallel with sending data to the client.
  215. new_controller_thread {
  216. ActiveSupport::Dependencies.interlock.running do
  217. t2 = Thread.current
  218. # Since we're processing the view in a different thread, copy the
  219. # thread locals from the main thread to the child thread. :'(
  220. locals.each { |k, v| t2[k] = v }
  221. begin
  222. super(name)
  223. rescue => e
  224. if @_response.committed?
  225. begin
  226. @_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
  227. @_response.stream.call_on_error
  228. rescue => exception
  229. log_error(exception)
  230. ensure
  231. log_error(e)
  232. @_response.stream.close
  233. end
  234. else
  235. error = e
  236. end
  237. ensure
  238. @_response.commit!
  239. end
  240. end
  241. }
  242. ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
  243. @_response.await_commit
  244. end
  245. raise error if error
  246. end
  247. 1 def response_body=(body)
  248. super
  249. response.close if response
  250. end
  251. 1 private
  252. # Spawn a new thread to serve up the controller in. This is to get
  253. # around the fact that Rack isn't based around IOs and we need to use
  254. # a thread to stream data from the response bodies. Nobody should call
  255. # this method except in Rails internals. Seriously!
  256. 1 def new_controller_thread # :nodoc:
  257. Thread.new {
  258. t2 = Thread.current
  259. t2.abort_on_exception = true
  260. yield
  261. }
  262. end
  263. 1 def log_error(exception)
  264. logger = ActionController::Base.logger
  265. return unless logger
  266. logger.fatal do
  267. message = +"\n#{exception.class} (#{exception.message}):\n"
  268. message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
  269. message << " " << exception.backtrace.join("\n ")
  270. "#{message}\n\n"
  271. end
  272. end
  273. end
  274. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/logging.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 module Logging
  4. 1 extend ActiveSupport::Concern
  5. 1 module ClassMethods
  6. # Set a different log level per request.
  7. #
  8. # # Use the debug log level if a particular cookie is set.
  9. # class ApplicationController < ActionController::Base
  10. # log_at :debug, if: -> { cookies[:debug] }
  11. # end
  12. #
  13. 1 def log_at(level, **options)
  14. around_action ->(_, action) { logger.log_at(level, &action) }, **options
  15. end
  16. end
  17. end
  18. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/mime_responds.rb

34.38% lines covered

64 relevant lines. 22 lines covered and 42 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "abstract_controller/collector"
  3. 1 module ActionController #:nodoc:
  4. 1 module MimeResponds
  5. # Without web-service support, an action which collects the data for displaying a list of people
  6. # might look something like this:
  7. #
  8. # def index
  9. # @people = Person.all
  10. # end
  11. #
  12. # That action implicitly responds to all formats, but formats can also be explicitly enumerated:
  13. #
  14. # def index
  15. # @people = Person.all
  16. # respond_to :html, :js
  17. # end
  18. #
  19. # Here's the same action, with web-service support baked in:
  20. #
  21. # def index
  22. # @people = Person.all
  23. #
  24. # respond_to do |format|
  25. # format.html
  26. # format.js
  27. # format.xml { render xml: @people }
  28. # end
  29. # end
  30. #
  31. # What that says is, "if the client wants HTML or JS in response to this action, just respond as we
  32. # would have before, but if the client wants XML, return them the list of people in XML format."
  33. # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
  34. #
  35. # Supposing you have an action that adds a new person, optionally creating their company
  36. # (by name) if it does not already exist, without web-services, it might look like this:
  37. #
  38. # def create
  39. # @company = Company.find_or_create_by(name: params[:company][:name])
  40. # @person = @company.people.create(params[:person])
  41. #
  42. # redirect_to(person_list_url)
  43. # end
  44. #
  45. # Here's the same action, with web-service support baked in:
  46. #
  47. # def create
  48. # company = params[:person].delete(:company)
  49. # @company = Company.find_or_create_by(name: company[:name])
  50. # @person = @company.people.create(params[:person])
  51. #
  52. # respond_to do |format|
  53. # format.html { redirect_to(person_list_url) }
  54. # format.js
  55. # format.xml { render xml: @person.to_xml(include: @company) }
  56. # end
  57. # end
  58. #
  59. # If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
  60. # then it is an Ajax request and we render the JavaScript template associated with this action.
  61. # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
  62. # include the person's company in the rendered XML, so you get something like this:
  63. #
  64. # <person>
  65. # <id>...</id>
  66. # ...
  67. # <company>
  68. # <id>...</id>
  69. # <name>...</name>
  70. # ...
  71. # </company>
  72. # </person>
  73. #
  74. # Note, however, the extra bit at the top of that action:
  75. #
  76. # company = params[:person].delete(:company)
  77. # @company = Company.find_or_create_by(name: company[:name])
  78. #
  79. # This is because the incoming XML document (if a web-service request is in process) can only contain a
  80. # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
  81. #
  82. # person[name]=...&person[company][name]=...&...
  83. #
  84. # And, like this (xml-encoded):
  85. #
  86. # <person>
  87. # <name>...</name>
  88. # <company>
  89. # <name>...</name>
  90. # </company>
  91. # </person>
  92. #
  93. # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
  94. # we extract the company data from the request, find or create the company, and then create the new person
  95. # with the remaining data.
  96. #
  97. # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
  98. # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
  99. # and accept Rails' defaults, life will be much easier.
  100. #
  101. # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
  102. # +config/initializers/mime_types.rb+ as follows.
  103. #
  104. # Mime::Type.register "image/jpg", :jpg
  105. #
  106. # +respond_to+ also allows you to specify a common block for different formats by using +any+:
  107. #
  108. # def index
  109. # @people = Person.all
  110. #
  111. # respond_to do |format|
  112. # format.html
  113. # format.any(:xml, :json) { render request.format.to_sym => @people }
  114. # end
  115. # end
  116. #
  117. # In the example above, if the format is xml, it will render:
  118. #
  119. # render xml: @people
  120. #
  121. # Or if the format is json:
  122. #
  123. # render json: @people
  124. #
  125. # +any+ can also be used with no arguments, in which case it will be used for any format requested by
  126. # the user:
  127. #
  128. # respond_to do |format|
  129. # format.html
  130. # format.any { redirect_to support_path }
  131. # end
  132. #
  133. # Formats can have different variants.
  134. #
  135. # The request variant is a specialization of the request format, like <tt>:tablet</tt>,
  136. # <tt>:phone</tt>, or <tt>:desktop</tt>.
  137. #
  138. # We often want to render different html/json/xml templates for phones,
  139. # tablets, and desktop browsers. Variants make it easy.
  140. #
  141. # You can set the variant in a +before_action+:
  142. #
  143. # request.variant = :tablet if /iPad/.match?(request.user_agent)
  144. #
  145. # Respond to variants in the action just like you respond to formats:
  146. #
  147. # respond_to do |format|
  148. # format.html do |variant|
  149. # variant.tablet # renders app/views/projects/show.html+tablet.erb
  150. # variant.phone { extra_setup; render ... }
  151. # variant.none { special_setup } # executed only if there is no variant set
  152. # end
  153. # end
  154. #
  155. # Provide separate templates for each format and variant:
  156. #
  157. # app/views/projects/show.html.erb
  158. # app/views/projects/show.html+tablet.erb
  159. # app/views/projects/show.html+phone.erb
  160. #
  161. # When you're not sharing any code within the format, you can simplify defining variants
  162. # using the inline syntax:
  163. #
  164. # respond_to do |format|
  165. # format.js { render "trash" }
  166. # format.html.phone { redirect_to progress_path }
  167. # format.html.none { render "trash" }
  168. # end
  169. #
  170. # Variants also support common +any+/+all+ block that formats have.
  171. #
  172. # It works for both inline:
  173. #
  174. # respond_to do |format|
  175. # format.html.any { render html: "any" }
  176. # format.html.phone { render html: "phone" }
  177. # end
  178. #
  179. # and block syntax:
  180. #
  181. # respond_to do |format|
  182. # format.html do |variant|
  183. # variant.any(:tablet, :phablet){ render html: "any" }
  184. # variant.phone { render html: "phone" }
  185. # end
  186. # end
  187. #
  188. # You can also set an array of variants:
  189. #
  190. # request.variant = [:tablet, :phone]
  191. #
  192. # This will work similarly to formats and MIME types negotiation. If there
  193. # is no +:tablet+ variant declared, the +:phone+ variant will be used:
  194. #
  195. # respond_to do |format|
  196. # format.html.none
  197. # format.html.phone # this gets rendered
  198. # end
  199. 1 def respond_to(*mimes)
  200. raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
  201. collector = Collector.new(mimes, request.variant)
  202. yield collector if block_given?
  203. if format = collector.negotiate_format(request)
  204. if media_type && media_type != format
  205. raise ActionController::RespondToMismatchError
  206. end
  207. _process_format(format)
  208. _set_rendered_content_type(format) unless collector.any_response?
  209. response = collector.response
  210. response.call if response
  211. else
  212. raise ActionController::UnknownFormat
  213. end
  214. end
  215. # A container for responses available from the current controller for
  216. # requests for different mime-types sent to a particular action.
  217. #
  218. # The public controller methods +respond_to+ may be called with a block
  219. # that is used to define responses to different mime-types, e.g.
  220. # for +respond_to+ :
  221. #
  222. # respond_to do |format|
  223. # format.html
  224. # format.xml { render xml: @people }
  225. # end
  226. #
  227. # In this usage, the argument passed to the block (+format+ above) is an
  228. # instance of the ActionController::MimeResponds::Collector class. This
  229. # object serves as a container in which available responses can be stored by
  230. # calling any of the dynamically generated, mime-type-specific methods such
  231. # as +html+, +xml+ etc on the Collector. Each response is represented by a
  232. # corresponding block if present.
  233. #
  234. # A subsequent call to #negotiate_format(request) will enable the Collector
  235. # to determine which specific mime-type it should respond with for the current
  236. # request, with this response then being accessible by calling #response.
  237. 1 class Collector
  238. 1 include AbstractController::Collector
  239. 1 attr_accessor :format
  240. 1 def initialize(mimes, variant = nil)
  241. @responses = {}
  242. @variant = variant
  243. mimes.each { |mime| @responses[Mime[mime]] = nil }
  244. end
  245. 1 def any(*args, &block)
  246. if args.any?
  247. args.each { |type| send(type, &block) }
  248. else
  249. custom(Mime::ALL, &block)
  250. end
  251. end
  252. 1 alias :all :any
  253. 1 def custom(mime_type, &block)
  254. mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
  255. @responses[mime_type] ||= if block_given?
  256. block
  257. else
  258. VariantCollector.new(@variant)
  259. end
  260. end
  261. 1 def any_response?
  262. !@responses.fetch(format, false) && @responses[Mime::ALL]
  263. end
  264. 1 def response
  265. response = @responses.fetch(format, @responses[Mime::ALL])
  266. if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
  267. response.variant
  268. elsif response.nil? || response.arity == 0 # `format.html` - just a format, call its block
  269. response
  270. else # `format.html{ |variant| variant.phone }` - variant block syntax
  271. variant_collector = VariantCollector.new(@variant)
  272. response.call(variant_collector) # call format block with variants collector
  273. variant_collector.variant
  274. end
  275. end
  276. 1 def negotiate_format(request)
  277. @format = request.negotiate_mime(@responses.keys)
  278. end
  279. 1 class VariantCollector #:nodoc:
  280. 1 def initialize(variant = nil)
  281. @variant = variant
  282. @variants = {}
  283. end
  284. 1 def any(*args, &block)
  285. if block_given?
  286. if args.any? && args.none? { |a| a == @variant }
  287. args.each { |v| @variants[v] = block }
  288. else
  289. @variants[:any] = block
  290. end
  291. end
  292. end
  293. 1 alias :all :any
  294. 1 def method_missing(name, *args, &block)
  295. @variants[name] = block if block_given?
  296. end
  297. 1 def variant
  298. if @variant.empty?
  299. @variants[:none] || @variants[:any]
  300. else
  301. @variants[variant_key]
  302. end
  303. end
  304. 1 private
  305. 1 def variant_key
  306. @variant.find { |variant| @variants.key?(variant) } || :any
  307. end
  308. end
  309. end
  310. end
  311. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/parameter_encoding.rb

75.0% lines covered

16 relevant lines. 12 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # Specify binary encoding for parameters for a given action.
  4. 1 module ParameterEncoding
  5. 1 extend ActiveSupport::Concern
  6. 1 module ClassMethods
  7. 1 def inherited(klass) # :nodoc:
  8. 1 super
  9. 1 klass.setup_param_encode
  10. end
  11. 1 def setup_param_encode # :nodoc:
  12. 1 @_parameter_encodings = Hash.new { |h, k| h[k] = {} }
  13. end
  14. 1 def action_encoding_template(action) # :nodoc:
  15. if @_parameter_encodings.has_key?(action.to_s)
  16. @_parameter_encodings[action.to_s]
  17. end
  18. end
  19. # Specify that a given action's parameters should all be encoded as
  20. # ASCII-8BIT (it "skips" the encoding default of UTF-8).
  21. #
  22. # For example, a controller would use it like this:
  23. #
  24. # class RepositoryController < ActionController::Base
  25. # skip_parameter_encoding :show
  26. #
  27. # def show
  28. # @repo = Repository.find_by_filesystem_path params[:file_path]
  29. #
  30. # # `repo_name` is guaranteed to be UTF-8, but was ASCII-8BIT, so
  31. # # tag it as such
  32. # @repo_name = params[:repo_name].force_encoding 'UTF-8'
  33. # end
  34. #
  35. # def index
  36. # @repositories = Repository.all
  37. # end
  38. # end
  39. #
  40. # The show action in the above controller would have all parameter values
  41. # encoded as ASCII-8BIT. This is useful in the case where an application
  42. # must handle data but encoding of the data is unknown, like file system data.
  43. 1 def skip_parameter_encoding(action)
  44. @_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT }
  45. end
  46. # Specify the encoding for a parameter on an action.
  47. # If not specified the default is UTF-8.
  48. #
  49. # You can specify a binary (ASCII_8BIT) parameter with:
  50. #
  51. # class RepositoryController < ActionController::Base
  52. # # This specifies that file_path is not UTF-8 and is instead ASCII_8BIT
  53. # param_encoding :show, :file_path, Encoding::ASCII_8BIT
  54. #
  55. # def show
  56. # @repo = Repository.find_by_filesystem_path params[:file_path]
  57. #
  58. # # params[:repo_name] remains UTF-8 encoded
  59. # @repo_name = params[:repo_name]
  60. # end
  61. #
  62. # def index
  63. # @repositories = Repository.all
  64. # end
  65. # end
  66. #
  67. # The file_path parameter on the show action would be encoded as ASCII-8BIT,
  68. # but all other arguments will remain UTF-8 encoded.
  69. # This is useful in the case where an application must handle data
  70. # but encoding of the data is unknown, like file system data.
  71. 1 def param_encoding(action, param, encoding)
  72. @_parameter_encodings[action.to_s][param.to_s] = encoding
  73. end
  74. end
  75. end
  76. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/params_wrapper.rb

50.46% lines covered

109 relevant lines. 55 lines covered and 54 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/hash/slice"
  3. 1 require "active_support/core_ext/hash/except"
  4. 1 require "active_support/core_ext/module/anonymous"
  5. 1 require "action_dispatch/http/mime_type"
  6. 1 module ActionController
  7. # Wraps the parameters hash into a nested hash. This will allow clients to
  8. # submit requests without having to specify any root elements.
  9. #
  10. # This functionality is enabled in +config/initializers/wrap_parameters.rb+
  11. # and can be customized.
  12. #
  13. # You could also turn it on per controller by setting the format array to
  14. # a non-empty array:
  15. #
  16. # class UsersController < ApplicationController
  17. # wrap_parameters format: [:json, :xml, :url_encoded_form, :multipart_form]
  18. # end
  19. #
  20. # If you enable +ParamsWrapper+ for +:json+ format, instead of having to
  21. # send JSON parameters like this:
  22. #
  23. # {"user": {"name": "Konata"}}
  24. #
  25. # You can send parameters like this:
  26. #
  27. # {"name": "Konata"}
  28. #
  29. # And it will be wrapped into a nested hash with the key name matching the
  30. # controller's name. For example, if you're posting to +UsersController+,
  31. # your new +params+ hash will look like this:
  32. #
  33. # {"name" => "Konata", "user" => {"name" => "Konata"}}
  34. #
  35. # You can also specify the key in which the parameters should be wrapped to,
  36. # and also the list of attributes it should wrap by using either +:include+ or
  37. # +:exclude+ options like this:
  38. #
  39. # class UsersController < ApplicationController
  40. # wrap_parameters :person, include: [:username, :password]
  41. # end
  42. #
  43. # On Active Record models with no +:include+ or +:exclude+ option set,
  44. # it will only wrap the parameters returned by the class method
  45. # <tt>attribute_names</tt>.
  46. #
  47. # If you're going to pass the parameters to an +ActiveModel+ object (such as
  48. # <tt>User.new(params[:user])</tt>), you might consider passing the model class to
  49. # the method instead. The +ParamsWrapper+ will actually try to determine the
  50. # list of attribute names from the model and only wrap those attributes:
  51. #
  52. # class UsersController < ApplicationController
  53. # wrap_parameters Person
  54. # end
  55. #
  56. # You still could pass +:include+ and +:exclude+ to set the list of attributes
  57. # you want to wrap.
  58. #
  59. # By default, if you don't specify the key in which the parameters would be
  60. # wrapped to, +ParamsWrapper+ will actually try to determine if there's
  61. # a model related to it or not. This controller, for example:
  62. #
  63. # class Admin::UsersController < ApplicationController
  64. # end
  65. #
  66. # will try to check if <tt>Admin::User</tt> or +User+ model exists, and use it to
  67. # determine the wrapper key respectively. If both models don't exist,
  68. # it will then fallback to use +user+ as the key.
  69. 1 module ParamsWrapper
  70. 1 extend ActiveSupport::Concern
  71. 1 EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
  72. 1 require "mutex_m"
  73. 1 class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc:
  74. 1 include Mutex_m
  75. 1 def self.from_hash(hash)
  76. 4 name = hash[:name]
  77. 4 format = Array(hash[:format])
  78. 4 include = hash[:include] && Array(hash[:include]).collect(&:to_s)
  79. 4 exclude = hash[:exclude] && Array(hash[:exclude]).collect(&:to_s)
  80. 4 new name, format, include, exclude, nil, nil
  81. end
  82. 1 def initialize(name, format, include, exclude, klass, model) # :nodoc:
  83. 4 super
  84. 4 @include_set = include
  85. 4 @name_set = name
  86. end
  87. 1 def model
  88. super || self.model = _default_wrap_model
  89. end
  90. 1 def include
  91. return super if @include_set
  92. m = model
  93. synchronize do
  94. return super if @include_set
  95. @include_set = true
  96. unless super || exclude
  97. if m.respond_to?(:attribute_names) && m.attribute_names.any?
  98. self.include = m.attribute_names
  99. if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
  100. self.include += m.stored_attributes.values.flatten.map(&:to_s)
  101. end
  102. if m.respond_to?(:attribute_aliases) && m.attribute_aliases.any?
  103. self.include += m.attribute_aliases.keys
  104. end
  105. if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
  106. self.include += m.nested_attributes_options.keys.map do |key|
  107. (+key.to_s).concat("_attributes")
  108. end
  109. end
  110. self.include
  111. end
  112. end
  113. end
  114. end
  115. 1 def name
  116. return super if @name_set
  117. m = model
  118. synchronize do
  119. return super if @name_set
  120. @name_set = true
  121. unless super || klass.anonymous?
  122. self.name = m ? m.to_s.demodulize.underscore :
  123. klass.controller_name.singularize
  124. end
  125. end
  126. end
  127. 1 private
  128. # Determine the wrapper model from the controller's name. By convention,
  129. # this could be done by trying to find the defined model that has the
  130. # same singular name as the controller. For example, +UsersController+
  131. # will try to find if the +User+ model exists.
  132. #
  133. # This method also does namespace lookup. Foo::Bar::UsersController will
  134. # try to find Foo::Bar::User, Foo::User and finally User.
  135. 1 def _default_wrap_model
  136. return nil if klass.anonymous?
  137. model_name = klass.name.delete_suffix("Controller").classify
  138. begin
  139. if model_klass = model_name.safe_constantize
  140. model_klass
  141. else
  142. namespaces = model_name.split("::")
  143. namespaces.delete_at(-2)
  144. break if namespaces.last == model_name
  145. model_name = namespaces.join("::")
  146. end
  147. end until model_klass
  148. model_klass
  149. end
  150. end
  151. 1 included do
  152. 2 class_attribute :_wrapper_options, default: Options.from_hash(format: [])
  153. end
  154. 1 module ClassMethods
  155. 1 def _set_wrapper_options(options)
  156. self._wrapper_options = Options.from_hash(options)
  157. end
  158. # Sets the name of the wrapper key, or the model which +ParamsWrapper+
  159. # would use to determine the attribute names from.
  160. #
  161. # ==== Examples
  162. # wrap_parameters format: :xml
  163. # # enables the parameter wrapper for XML format
  164. #
  165. # wrap_parameters :person
  166. # # wraps parameters into +params[:person]+ hash
  167. #
  168. # wrap_parameters Person
  169. # # wraps parameters by determining the wrapper key from Person class
  170. # # (+person+, in this case) and the list of attribute names
  171. #
  172. # wrap_parameters include: [:username, :title]
  173. # # wraps only +:username+ and +:title+ attributes from parameters.
  174. #
  175. # wrap_parameters false
  176. # # disables parameters wrapping for this controller altogether.
  177. #
  178. # ==== Options
  179. # * <tt>:format</tt> - The list of formats in which the parameters wrapper
  180. # will be enabled.
  181. # * <tt>:include</tt> - The list of attribute names which parameters wrapper
  182. # will wrap into a nested hash.
  183. # * <tt>:exclude</tt> - The list of attribute names which parameters wrapper
  184. # will exclude from a nested hash.
  185. 1 def wrap_parameters(name_or_model_or_options, options = {})
  186. 2 model = nil
  187. 2 case name_or_model_or_options
  188. when Hash
  189. 2 options = name_or_model_or_options
  190. when false
  191. options = options.merge(format: [])
  192. when Symbol, String
  193. options = options.merge(name: name_or_model_or_options)
  194. else
  195. model = name_or_model_or_options
  196. end
  197. 2 opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
  198. 2 opts.model = model
  199. 2 opts.klass = self
  200. 2 self._wrapper_options = opts
  201. end
  202. # Sets the default wrapper key or model which will be used to determine
  203. # wrapper key and attribute names. Called automatically when the
  204. # module is inherited.
  205. 1 def inherited(klass)
  206. 3 if klass._wrapper_options.format.any?
  207. 3 params = klass._wrapper_options.dup
  208. 3 params.klass = klass
  209. 3 klass._wrapper_options = params
  210. end
  211. 3 super
  212. end
  213. end
  214. # Performs parameters wrapping upon the request. Called automatically
  215. # by the metal call stack.
  216. 1 def process_action(*)
  217. 1 _perform_parameter_wrapping if _wrapper_enabled?
  218. 1 super
  219. end
  220. 1 private
  221. # Returns the wrapper key which will be used to store wrapped parameters.
  222. 1 def _wrapper_key
  223. _wrapper_options.name
  224. end
  225. # Returns the list of enabled formats.
  226. 1 def _wrapper_formats
  227. _wrapper_options.format
  228. end
  229. # Returns the list of parameters which will be selected for wrapped.
  230. 1 def _wrap_parameters(parameters)
  231. { _wrapper_key => _extract_parameters(parameters) }
  232. end
  233. 1 def _extract_parameters(parameters)
  234. if include_only = _wrapper_options.include
  235. parameters.slice(*include_only)
  236. elsif _wrapper_options.exclude
  237. exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
  238. parameters.except(*exclude)
  239. else
  240. parameters.except(*EXCLUDE_PARAMETERS)
  241. end
  242. end
  243. # Checks if we should perform parameters wrapping.
  244. 1 def _wrapper_enabled?
  245. 1 return false unless request.has_content_type?
  246. ref = request.content_mime_type.ref
  247. _wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
  248. end
  249. 1 def _perform_parameter_wrapping
  250. wrapped_hash = _wrap_parameters request.request_parameters
  251. wrapped_keys = request.request_parameters.keys
  252. wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
  253. # This will make the wrapped hash accessible from controller and view.
  254. request.parameters.merge! wrapped_hash
  255. request.request_parameters.merge! wrapped_hash
  256. # This will display the wrapped hash in the log file.
  257. request.filtered_parameters.merge! wrapped_filtered_hash
  258. rescue ActionDispatch::Http::Parameters::ParseError
  259. # swallow parse error exception
  260. end
  261. end
  262. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/permissions_policy.rb

50.0% lines covered

10 relevant lines. 5 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController #:nodoc:
  3. # HTTP Permissions Policy is a web standard for defining a mechanism to
  4. # allow and deny the use of browser permissions in its own context, and
  5. # in content within any <iframe> elements in the document.
  6. #
  7. # Full details of HTTP Permissions Policy specification and guidelines can
  8. # be found at MDN:
  9. #
  10. # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
  11. #
  12. # Examples of usage:
  13. #
  14. # # Global policy
  15. # Rails.application.config.permissions_policy do |f|
  16. # f.camera :none
  17. # f.gyroscope :none
  18. # f.microphone :none
  19. # f.usb :none
  20. # f.fullscreen :self
  21. # f.payment :self, "https://secure.example.com"
  22. # end
  23. #
  24. # # Controller level policy
  25. # class PagesController < ApplicationController
  26. # permissions_policy do |p|
  27. # p.geolocation "https://example.com"
  28. # end
  29. # end
  30. 1 module PermissionsPolicy
  31. 1 extend ActiveSupport::Concern
  32. 1 module ClassMethods
  33. 1 def permissions_policy(**options, &block)
  34. before_action(options) do
  35. if block_given?
  36. policy = request.permissions_policy.clone
  37. yield policy
  38. request.permissions_policy = policy
  39. end
  40. end
  41. end
  42. end
  43. end
  44. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/redirecting.rb

39.39% lines covered

33 relevant lines. 13 lines covered and 20 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 module Redirecting
  4. 1 extend ActiveSupport::Concern
  5. 1 include AbstractController::Logger
  6. 1 include ActionController::UrlFor
  7. # Redirects the browser to the target specified in +options+. This parameter can be any one of:
  8. #
  9. # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
  10. # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
  11. # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection.
  12. # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
  13. # * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
  14. #
  15. # === Examples:
  16. #
  17. # redirect_to action: "show", id: 5
  18. # redirect_to @post
  19. # redirect_to "http://www.rubyonrails.org"
  20. # redirect_to "/images/screenshot.jpg"
  21. # redirect_to posts_url
  22. # redirect_to proc { edit_post_url(@post) }
  23. #
  24. # The redirection happens as a <tt>302 Found</tt> header unless otherwise specified using the <tt>:status</tt> option:
  25. #
  26. # redirect_to post_url(@post), status: :found
  27. # redirect_to action: 'atom', status: :moved_permanently
  28. # redirect_to post_url(@post), status: 301
  29. # redirect_to action: 'atom', status: 302
  30. #
  31. # The status code can either be a standard {HTTP Status code}[https://www.iana.org/assignments/http-status-codes] as an
  32. # integer, or a symbol representing the downcased, underscored and symbolized description.
  33. # Note that the status code must be a 3xx HTTP code, or redirection will not occur.
  34. #
  35. # If you are using XHR requests other than GET or POST and redirecting after the
  36. # request then some browsers will follow the redirect using the original request
  37. # method. This may lead to undesirable behavior such as a double DELETE. To work
  38. # around this you can return a <tt>303 See Other</tt> status code which will be
  39. # followed using a GET request.
  40. #
  41. # redirect_to posts_url, status: :see_other
  42. # redirect_to action: 'index', status: 303
  43. #
  44. # It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
  45. # +alert+ and +notice+ as well as a general purpose +flash+ bucket.
  46. #
  47. # redirect_to post_url(@post), alert: "Watch it, mister!"
  48. # redirect_to post_url(@post), status: :found, notice: "Pay attention to the road"
  49. # redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
  50. # redirect_to({ action: 'atom' }, alert: "Something serious happened")
  51. #
  52. # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
  53. # To terminate the execution of the function immediately after the +redirect_to+, use return.
  54. # redirect_to post_url(@post) and return
  55. 1 def redirect_to(options = {}, response_options = {})
  56. raise ActionControllerError.new("Cannot redirect to nil!") unless options
  57. raise AbstractController::DoubleRenderError if response_body
  58. self.status = _extract_redirect_to_status(options, response_options)
  59. self.location = _compute_redirect_to_location(request, options)
  60. self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
  61. end
  62. # Redirects the browser to the page that issued the request (the referrer)
  63. # if possible, otherwise redirects to the provided default fallback
  64. # location.
  65. #
  66. # The referrer information is pulled from the HTTP +Referer+ (sic) header on
  67. # the request. This is an optional header and its presence on the request is
  68. # subject to browser security settings and user preferences. If the request
  69. # is missing this header, the <tt>fallback_location</tt> will be used.
  70. #
  71. # redirect_back fallback_location: { action: "show", id: 5 }
  72. # redirect_back fallback_location: @post
  73. # redirect_back fallback_location: "http://www.rubyonrails.org"
  74. # redirect_back fallback_location: "/images/screenshot.jpg"
  75. # redirect_back fallback_location: posts_url
  76. # redirect_back fallback_location: proc { edit_post_url(@post) }
  77. # redirect_back fallback_location: '/', allow_other_host: false
  78. #
  79. # ==== Options
  80. # * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
  81. # * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
  82. #
  83. # All other options that can be passed to #redirect_to are accepted as
  84. # options and the behavior is identical.
  85. 1 def redirect_back(fallback_location:, allow_other_host: true, **args)
  86. referer = request.headers["Referer"]
  87. redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer))
  88. redirect_to redirect_to_referer ? referer : fallback_location, **args
  89. end
  90. 1 def _compute_redirect_to_location(request, options) #:nodoc:
  91. case options
  92. # The scheme name consist of a letter followed by any combination of
  93. # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
  94. # characters; and is terminated by a colon (":").
  95. # See https://tools.ietf.org/html/rfc3986#section-3.1
  96. # The protocol relative scheme starts with a double slash "//".
  97. when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
  98. options
  99. when String
  100. request.protocol + request.host_with_port + options
  101. when Proc
  102. _compute_redirect_to_location request, instance_eval(&options)
  103. else
  104. url_for(options)
  105. end.delete("\0\r\n")
  106. end
  107. 1 module_function :_compute_redirect_to_location
  108. 1 public :_compute_redirect_to_location
  109. 1 private
  110. 1 def _extract_redirect_to_status(options, response_options)
  111. if options.is_a?(Hash) && options.key?(:status)
  112. Rack::Utils.status_code(options.delete(:status))
  113. elsif response_options.key?(:status)
  114. Rack::Utils.status_code(response_options[:status])
  115. else
  116. 302
  117. end
  118. end
  119. 1 def _url_host_allowed?(url)
  120. URI(url.to_s).host == request.host
  121. rescue ArgumentError, URI::Error
  122. false
  123. end
  124. end
  125. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/renderers.rb

72.73% lines covered

55 relevant lines. 40 lines covered and 15 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "set"
  3. 1 module ActionController
  4. # See <tt>Renderers.add</tt>
  5. 1 def self.add_renderer(key, &block)
  6. Renderers.add(key, &block)
  7. end
  8. # See <tt>Renderers.remove</tt>
  9. 1 def self.remove_renderer(key)
  10. Renderers.remove(key)
  11. end
  12. # See <tt>Responder#api_behavior</tt>
  13. 1 class MissingRenderer < LoadError
  14. 1 def initialize(format)
  15. super "No renderer defined for format: #{format}"
  16. end
  17. end
  18. 1 module Renderers
  19. 1 extend ActiveSupport::Concern
  20. # A Set containing renderer names that correspond to available renderer procs.
  21. # Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
  22. 1 RENDERERS = Set.new
  23. 1 included do
  24. 2 class_attribute :_renderers, default: Set.new.freeze
  25. end
  26. # Used in <tt>ActionController::Base</tt>
  27. # and <tt>ActionController::API</tt> to include all
  28. # renderers by default.
  29. 1 module All
  30. 1 extend ActiveSupport::Concern
  31. 1 include Renderers
  32. 1 included do
  33. 2 self._renderers = RENDERERS
  34. end
  35. end
  36. # Adds a new renderer to call within controller actions.
  37. # A renderer is invoked by passing its name as an option to
  38. # <tt>AbstractController::Rendering#render</tt>. To create a renderer
  39. # pass it a name and a block. The block takes two arguments, the first
  40. # is the value paired with its key and the second is the remaining
  41. # hash of options passed to +render+.
  42. #
  43. # Create a csv renderer:
  44. #
  45. # ActionController::Renderers.add :csv do |obj, options|
  46. # filename = options[:filename] || 'data'
  47. # str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
  48. # send_data str, type: Mime[:csv],
  49. # disposition: "attachment; filename=#{filename}.csv"
  50. # end
  51. #
  52. # Note that we used Mime[:csv] for the csv mime type as it comes with Rails.
  53. # For a custom renderer, you'll need to register a mime type with
  54. # <tt>Mime::Type.register</tt>.
  55. #
  56. # To use the csv renderer in a controller action:
  57. #
  58. # def show
  59. # @csvable = Csvable.find(params[:id])
  60. # respond_to do |format|
  61. # format.html
  62. # format.csv { render csv: @csvable, filename: @csvable.name }
  63. # end
  64. # end
  65. 1 def self.add(key, &block)
  66. 3 define_method(_render_with_renderer_method_name(key), &block)
  67. 3 RENDERERS << key.to_sym
  68. end
  69. # This method is the opposite of add method.
  70. #
  71. # To remove a csv renderer:
  72. #
  73. # ActionController::Renderers.remove(:csv)
  74. 1 def self.remove(key)
  75. RENDERERS.delete(key.to_sym)
  76. method_name = _render_with_renderer_method_name(key)
  77. remove_possible_method(method_name)
  78. end
  79. 1 def self._render_with_renderer_method_name(key)
  80. 4 "_render_with_renderer_#{key}"
  81. end
  82. 1 module ClassMethods
  83. # Adds, by name, a renderer or renderers to the +_renderers+ available
  84. # to call within controller actions.
  85. #
  86. # It is useful when rendering from an <tt>ActionController::Metal</tt> controller or
  87. # otherwise to add an available renderer proc to a specific controller.
  88. #
  89. # Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
  90. # include <tt>ActionController::Renderers::All</tt>, making all renderers
  91. # available in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
  92. #
  93. # Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
  94. # must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
  95. # and <tt>ActionController::Renderers</tt>, and have at least one renderer.
  96. #
  97. # Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
  98. # you may specify which renderers to include by passing the renderer name or names to
  99. # +use_renderers+. For example, a controller that includes only the <tt>:json</tt> renderer
  100. # (+_render_with_renderer_json+) might look like:
  101. #
  102. # class MetalRenderingController < ActionController::Metal
  103. # include AbstractController::Rendering
  104. # include ActionController::Rendering
  105. # include ActionController::Renderers
  106. #
  107. # use_renderers :json
  108. #
  109. # def show
  110. # render json: record
  111. # end
  112. # end
  113. #
  114. # You must specify a +use_renderer+, else the +controller.renderer+ and
  115. # +controller._renderers+ will be <tt>nil</tt>, and the action will fail.
  116. 1 def use_renderers(*args)
  117. renderers = _renderers + args
  118. self._renderers = renderers.freeze
  119. end
  120. 1 alias use_renderer use_renderers
  121. end
  122. # Called by +render+ in <tt>AbstractController::Rendering</tt>
  123. # which sets the return value as the +response_body+.
  124. #
  125. # If no renderer is found, +super+ returns control to
  126. # <tt>ActionView::Rendering.render_to_body</tt>, if present.
  127. 1 def render_to_body(options)
  128. 1 _render_to_body_with_renderer(options) || super
  129. end
  130. 1 def _render_to_body_with_renderer(options)
  131. 1 _renderers.each do |name|
  132. 1 if options.key?(name)
  133. 1 _process_options(options)
  134. 1 method_name = Renderers._render_with_renderer_method_name(name)
  135. 1 return send(method_name, options.delete(name), options)
  136. end
  137. end
  138. nil
  139. end
  140. 1 add :json do |json, options|
  141. 1 json = json.to_json(options) unless json.kind_of?(String)
  142. 1 if options[:callback].present?
  143. if media_type.nil? || media_type == Mime[:json]
  144. self.content_type = Mime[:js]
  145. end
  146. "/**/#{options[:callback]}(#{json})"
  147. else
  148. 1 self.content_type = Mime[:json] if media_type.nil?
  149. 1 json
  150. end
  151. end
  152. 1 add :js do |js, options|
  153. self.content_type = Mime[:js] if media_type.nil?
  154. js.respond_to?(:to_js) ? js.to_js(options) : js
  155. end
  156. 1 add :xml do |xml, options|
  157. self.content_type = Mime[:xml] if media_type.nil?
  158. xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
  159. end
  160. end
  161. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/rendering.rb

76.19% lines covered

63 relevant lines. 48 lines covered and 15 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 module Rendering
  4. 1 extend ActiveSupport::Concern
  5. 1 RENDER_FORMATS_IN_PRIORITY = [:body, :plain, :html]
  6. 1 module ClassMethods
  7. # Documentation at ActionController::Renderer#render
  8. 1 delegate :render, to: :renderer
  9. # Returns a renderer instance (inherited from ActionController::Renderer)
  10. # for the controller.
  11. 1 attr_reader :renderer
  12. 1 def setup_renderer! # :nodoc:
  13. 4 @renderer = Renderer.for(self)
  14. end
  15. 1 def inherited(klass)
  16. 3 klass.setup_renderer!
  17. 3 super
  18. end
  19. end
  20. # Before processing, set the request formats in current controller formats.
  21. 1 def process_action(*) #:nodoc:
  22. 1 self.formats = request.formats.map(&:ref).compact
  23. 1 super
  24. end
  25. # Check for double render errors and set the content_type after rendering.
  26. 1 def render(*args) #:nodoc:
  27. 1 raise ::AbstractController::DoubleRenderError if response_body
  28. 1 super
  29. end
  30. # Overwrite render_to_string because body can now be set to a Rack body.
  31. 1 def render_to_string(*)
  32. result = super
  33. if result.respond_to?(:each)
  34. string = +""
  35. result.each { |r| string << r }
  36. string
  37. else
  38. result
  39. end
  40. end
  41. 1 def render_to_body(options = {})
  42. super || _render_in_priorities(options) || " "
  43. end
  44. 1 private
  45. 1 def _process_variant(options)
  46. 1 if defined?(request) && !request.nil? && request.variant.present?
  47. options[:variant] = request.variant
  48. end
  49. end
  50. 1 def _render_in_priorities(options)
  51. RENDER_FORMATS_IN_PRIORITY.each do |format|
  52. return options[format] if options.key?(format)
  53. end
  54. nil
  55. end
  56. 1 def _set_html_content_type
  57. self.content_type = Mime[:html].to_s
  58. end
  59. 1 def _set_rendered_content_type(format)
  60. 1 if format && !response.media_type
  61. self.content_type = format.to_s
  62. end
  63. end
  64. 1 def _set_vary_header
  65. 1 if self.headers["Vary"].blank? && request.should_apply_vary_header?
  66. self.headers["Vary"] = "Accept"
  67. end
  68. end
  69. # Normalize arguments by catching blocks and setting them on :update.
  70. 1 def _normalize_args(action = nil, options = {}, &blk)
  71. 1 options = super
  72. 1 options[:update] = blk if block_given?
  73. 1 options
  74. end
  75. # Normalize both text and status options.
  76. 1 def _normalize_options(options)
  77. 1 _normalize_text(options)
  78. 1 if options[:html]
  79. options[:html] = ERB::Util.html_escape(options[:html])
  80. end
  81. 1 if options[:status]
  82. 1 options[:status] = Rack::Utils.status_code(options[:status])
  83. end
  84. 1 super
  85. end
  86. 1 def _normalize_text(options)
  87. 1 RENDER_FORMATS_IN_PRIORITY.each do |format|
  88. 3 if options.key?(format) && options[format].respond_to?(:to_text)
  89. options[format] = options[format].to_text
  90. end
  91. end
  92. end
  93. # Process controller specific options, as status, content-type and location.
  94. 1 def _process_options(options)
  95. 1 status, content_type, location = options.values_at(:status, :content_type, :location)
  96. 1 self.status = status if status
  97. 1 self.content_type = content_type if content_type
  98. 1 headers["Location"] = url_for(location) if location
  99. 1 super
  100. end
  101. end
  102. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/request_forgery_protection.rb

48.95% lines covered

190 relevant lines. 93 lines covered and 97 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rack/session/abstract/id"
  3. 1 require "action_controller/metal/exceptions"
  4. 1 require "active_support/security_utils"
  5. 1 module ActionController #:nodoc:
  6. 1 class InvalidAuthenticityToken < ActionControllerError #:nodoc:
  7. end
  8. 1 class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
  9. end
  10. # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
  11. # by including a token in the rendered HTML for your application. This token is
  12. # stored as a random string in the session, to which an attacker does not have
  13. # access. When a request reaches your application, \Rails verifies the received
  14. # token with the token in the session. All requests are checked except GET requests
  15. # as these should be idempotent. Keep in mind that all session-oriented requests
  16. # are CSRF protected by default, including JavaScript and HTML requests.
  17. #
  18. # Since HTML and JavaScript requests are typically made from the browser, we
  19. # need to ensure to verify request authenticity for the web browser. We can
  20. # use session-oriented authentication for these types of requests, by using
  21. # the <tt>protect_from_forgery</tt> method in our controllers.
  22. #
  23. # GET requests are not protected since they don't have side effects like writing
  24. # to the database and don't leak sensitive information. JavaScript requests are
  25. # an exception: a third-party site can use a <script> tag to reference a JavaScript
  26. # URL on your site. When your JavaScript response loads on their site, it executes.
  27. # With carefully crafted JavaScript on their end, sensitive data in your JavaScript
  28. # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
  29. # Ajax) requests are allowed to make requests for JavaScript responses.
  30. #
  31. # Subclasses of <tt>ActionController::Base</tt> are protected by default with the
  32. # <tt>:exception</tt> strategy, which raises an
  33. # <tt>ActionController::InvalidAuthenticityToken</tt> error on unverified requests.
  34. #
  35. # APIs may want to disable this behavior since they are typically designed to be
  36. # state-less: that is, the request API client handles the session instead of Rails.
  37. # One way to achieve this is to use the <tt>:null_session</tt> strategy instead,
  38. # which allows unverified requests to be handled, but with an empty session:
  39. #
  40. # class ApplicationController < ActionController::Base
  41. # protect_from_forgery with: :null_session
  42. # end
  43. #
  44. # Note that API only applications don't include this module or a session middleware
  45. # by default, and so don't require CSRF protection to be configured.
  46. #
  47. # The token parameter is named <tt>authenticity_token</tt> by default. The name and
  48. # value of this token must be added to every layout that renders forms by including
  49. # <tt>csrf_meta_tags</tt> in the HTML +head+.
  50. #
  51. # Learn more about CSRF attacks and securing your application in the
  52. # {Ruby on Rails Security Guide}[https://guides.rubyonrails.org/security.html].
  53. 1 module RequestForgeryProtection
  54. 1 extend ActiveSupport::Concern
  55. 1 include AbstractController::Helpers
  56. 1 include AbstractController::Callbacks
  57. 1 included do
  58. # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
  59. # sets it to <tt>:authenticity_token</tt> by default.
  60. 1 config_accessor :request_forgery_protection_token
  61. 1 self.request_forgery_protection_token ||= :authenticity_token
  62. # Holds the class which implements the request forgery protection.
  63. 1 config_accessor :forgery_protection_strategy
  64. 1 self.forgery_protection_strategy = nil
  65. # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
  66. 1 config_accessor :allow_forgery_protection
  67. 1 self.allow_forgery_protection = true if allow_forgery_protection.nil?
  68. # Controls whether a CSRF failure logs a warning. On by default.
  69. 1 config_accessor :log_warning_on_csrf_failure
  70. 1 self.log_warning_on_csrf_failure = true
  71. # Controls whether the Origin header is checked in addition to the CSRF token.
  72. 1 config_accessor :forgery_protection_origin_check
  73. 1 self.forgery_protection_origin_check = false
  74. # Controls whether form-action/method specific CSRF tokens are used.
  75. 1 config_accessor :per_form_csrf_tokens
  76. 1 self.per_form_csrf_tokens = false
  77. # Controls whether forgery protection is enabled by default.
  78. 1 config_accessor :default_protect_from_forgery
  79. 1 self.default_protect_from_forgery = false
  80. # Controls whether URL-safe CSRF tokens are generated.
  81. 1 config_accessor :urlsafe_csrf_tokens, instance_writer: false
  82. 1 self.urlsafe_csrf_tokens = false
  83. 1 helper_method :form_authenticity_token
  84. 1 helper_method :protect_against_forgery?
  85. end
  86. 1 module ClassMethods
  87. # Turn on request forgery protection. Bear in mind that GET and HEAD requests are not checked.
  88. #
  89. # class ApplicationController < ActionController::Base
  90. # protect_from_forgery
  91. # end
  92. #
  93. # class FooController < ApplicationController
  94. # protect_from_forgery except: :index
  95. # end
  96. #
  97. # You can disable forgery protection on controller by skipping the verification before_action:
  98. #
  99. # skip_before_action :verify_authenticity_token
  100. #
  101. # Valid Options:
  102. #
  103. # * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>.
  104. # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
  105. # * <tt>:prepend</tt> - By default, the verification of the authentication token will be added at the position of the
  106. # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful
  107. # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth).
  108. #
  109. # If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
  110. # * <tt>:with</tt> - Set the method to handle unverified request.
  111. #
  112. # Valid unverified request handling methods are:
  113. # * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
  114. # * <tt>:reset_session</tt> - Resets the session.
  115. # * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
  116. 1 def protect_from_forgery(options = {})
  117. 1 options = options.reverse_merge(prepend: false)
  118. 1 self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
  119. 1 self.request_forgery_protection_token ||= :authenticity_token
  120. 1 before_action :verify_authenticity_token, options
  121. 1 append_after_action :verify_same_origin_request
  122. end
  123. # Turn off request forgery protection. This is a wrapper for:
  124. #
  125. # skip_before_action :verify_authenticity_token
  126. #
  127. # See +skip_before_action+ for allowed options.
  128. 1 def skip_forgery_protection(options = {})
  129. skip_before_action :verify_authenticity_token, options
  130. end
  131. 1 private
  132. 1 def protection_method_class(name)
  133. 1 ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
  134. rescue NameError
  135. raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, or :reset_session"
  136. end
  137. end
  138. 1 module ProtectionMethods
  139. 1 class NullSession
  140. 1 def initialize(controller)
  141. @controller = controller
  142. end
  143. # This is the method that defines the application behavior when a request is found to be unverified.
  144. 1 def handle_unverified_request
  145. request = @controller.request
  146. request.session = NullSessionHash.new(request)
  147. request.flash = nil
  148. request.session_options = { skip: true }
  149. request.cookie_jar = NullCookieJar.build(request, {})
  150. end
  151. 1 private
  152. 1 class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
  153. 1 def initialize(req)
  154. super(nil, req)
  155. @data = {}
  156. @loaded = true
  157. end
  158. # no-op
  159. 1 def destroy; end
  160. 1 def exists?
  161. true
  162. end
  163. end
  164. 1 class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
  165. 1 def write(*)
  166. # nothing
  167. end
  168. end
  169. end
  170. 1 class ResetSession
  171. 1 def initialize(controller)
  172. @controller = controller
  173. end
  174. 1 def handle_unverified_request
  175. @controller.reset_session
  176. end
  177. end
  178. 1 class Exception
  179. 1 def initialize(controller)
  180. @controller = controller
  181. end
  182. 1 def handle_unverified_request
  183. raise ActionController::InvalidAuthenticityToken
  184. end
  185. end
  186. end
  187. 1 private
  188. # The actual before_action that is used to verify the CSRF token.
  189. # Don't override this directly. Provide your own forgery protection
  190. # strategy instead. If you override, you'll disable same-origin
  191. # <tt><script></tt> verification.
  192. #
  193. # Lean on the protect_from_forgery declaration to mark which actions are
  194. # due for same-origin request verification. If protect_from_forgery is
  195. # enabled on an action, this before_action flags its after_action to
  196. # verify that JavaScript responses are for XHR requests, ensuring they
  197. # follow the browser's same-origin policy.
  198. 1 def verify_authenticity_token # :doc:
  199. mark_for_same_origin_verification!
  200. if !verified_request?
  201. if logger && log_warning_on_csrf_failure
  202. if valid_request_origin?
  203. logger.warn "Can't verify CSRF token authenticity."
  204. else
  205. logger.warn "HTTP Origin header (#{request.origin}) didn't match request.base_url (#{request.base_url})"
  206. end
  207. end
  208. handle_unverified_request
  209. end
  210. end
  211. 1 def handle_unverified_request # :doc:
  212. forgery_protection_strategy.new(self).handle_unverified_request
  213. end
  214. #:nodoc:
  215. 1 CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
  216. "<script> tag on another site requested protected JavaScript. " \
  217. "If you know what you're doing, go ahead and disable forgery " \
  218. "protection on this action to permit cross-origin JavaScript embedding."
  219. 1 private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
  220. # :startdoc:
  221. # If +verify_authenticity_token+ was run (indicating that we have
  222. # forgery protection enabled for this request) then also verify that
  223. # we aren't serving an unauthorized cross-origin response.
  224. 1 def verify_same_origin_request # :doc:
  225. if marked_for_same_origin_verification? && non_xhr_javascript_response?
  226. if logger && log_warning_on_csrf_failure
  227. logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING
  228. end
  229. raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING
  230. end
  231. end
  232. # GET requests are checked for cross-origin JavaScript after rendering.
  233. 1 def mark_for_same_origin_verification! # :doc:
  234. @marked_for_same_origin_verification = request.get?
  235. end
  236. # If the +verify_authenticity_token+ before_action ran, verify that
  237. # JavaScript responses are only served to same-origin GET requests.
  238. 1 def marked_for_same_origin_verification? # :doc:
  239. @marked_for_same_origin_verification ||= false
  240. end
  241. # Check for cross-origin JavaScript responses.
  242. 1 def non_xhr_javascript_response? # :doc:
  243. %r(\A(?:text|application)/javascript).match?(media_type) && !request.xhr?
  244. end
  245. 1 AUTHENTICITY_TOKEN_LENGTH = 32
  246. # Returns true or false if a request is verified. Checks:
  247. #
  248. # * Is it a GET or HEAD request? GETs should be safe and idempotent
  249. # * Does the form_authenticity_token match the given token value from the params?
  250. # * Does the X-CSRF-Token header match the form_authenticity_token?
  251. 1 def verified_request? # :doc:
  252. !protect_against_forgery? || request.get? || request.head? ||
  253. (valid_request_origin? && any_authenticity_token_valid?)
  254. end
  255. # Checks if any of the authenticity tokens from the request are valid.
  256. 1 def any_authenticity_token_valid? # :doc:
  257. request_authenticity_tokens.any? do |token|
  258. valid_authenticity_token?(session, token)
  259. end
  260. end
  261. # Possible authenticity tokens sent in the request.
  262. 1 def request_authenticity_tokens # :doc:
  263. [form_authenticity_param, request.x_csrf_token]
  264. end
  265. # Sets the token value for the current session.
  266. 1 def form_authenticity_token(form_options: {})
  267. masked_authenticity_token(session, form_options: form_options)
  268. end
  269. # Creates a masked version of the authenticity token that varies
  270. # on each request. The masking is used to mitigate SSL attacks
  271. # like BREACH.
  272. 1 def masked_authenticity_token(session, form_options: {}) # :doc:
  273. action, method = form_options.values_at(:action, :method)
  274. raw_token = if per_form_csrf_tokens && action && method
  275. action_path = normalize_action_path(action)
  276. per_form_csrf_token(session, action_path, method)
  277. else
  278. global_csrf_token(session)
  279. end
  280. mask_token(raw_token)
  281. end
  282. # Checks the client's masked token to see if it matches the
  283. # session token. Essentially the inverse of
  284. # +masked_authenticity_token+.
  285. 1 def valid_authenticity_token?(session, encoded_masked_token) # :doc:
  286. if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String)
  287. return false
  288. end
  289. begin
  290. masked_token = decode_csrf_token(encoded_masked_token)
  291. rescue ArgumentError # encoded_masked_token is invalid Base64
  292. return false
  293. end
  294. # See if it's actually a masked token or not. In order to
  295. # deploy this code, we should be able to handle any unmasked
  296. # tokens that we've issued without error.
  297. if masked_token.length == AUTHENTICITY_TOKEN_LENGTH
  298. # This is actually an unmasked token. This is expected if
  299. # you have just upgraded to masked tokens, but should stop
  300. # happening shortly after installing this gem.
  301. compare_with_real_token masked_token, session
  302. elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
  303. csrf_token = unmask_token(masked_token)
  304. compare_with_global_token(csrf_token, session) ||
  305. compare_with_real_token(csrf_token, session) ||
  306. valid_per_form_csrf_token?(csrf_token, session)
  307. else
  308. false # Token is malformed.
  309. end
  310. end
  311. 1 def unmask_token(masked_token) # :doc:
  312. # Split the token into the one-time pad and the encrypted
  313. # value and decrypt it.
  314. one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
  315. encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
  316. xor_byte_strings(one_time_pad, encrypted_csrf_token)
  317. end
  318. 1 def mask_token(raw_token) # :doc:
  319. one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
  320. encrypted_csrf_token = xor_byte_strings(one_time_pad, raw_token)
  321. masked_token = one_time_pad + encrypted_csrf_token
  322. encode_csrf_token(masked_token)
  323. end
  324. 1 def compare_with_real_token(token, session) # :doc:
  325. ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, real_csrf_token(session))
  326. end
  327. 1 def compare_with_global_token(token, session) # :doc:
  328. ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, global_csrf_token(session))
  329. end
  330. 1 def valid_per_form_csrf_token?(token, session) # :doc:
  331. if per_form_csrf_tokens
  332. correct_token = per_form_csrf_token(
  333. session,
  334. request.path.chomp("/"),
  335. request.request_method
  336. )
  337. ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, correct_token)
  338. else
  339. false
  340. end
  341. end
  342. 1 def real_csrf_token(session) # :doc:
  343. session[:_csrf_token] ||= generate_csrf_token
  344. decode_csrf_token(session[:_csrf_token])
  345. end
  346. 1 def per_form_csrf_token(session, action_path, method) # :doc:
  347. csrf_token_hmac(session, [action_path, method.downcase].join("#"))
  348. end
  349. 1 GLOBAL_CSRF_TOKEN_IDENTIFIER = "!real_csrf_token"
  350. 1 private_constant :GLOBAL_CSRF_TOKEN_IDENTIFIER
  351. 1 def global_csrf_token(session) # :doc:
  352. csrf_token_hmac(session, GLOBAL_CSRF_TOKEN_IDENTIFIER)
  353. end
  354. 1 def csrf_token_hmac(session, identifier) # :doc:
  355. OpenSSL::HMAC.digest(
  356. OpenSSL::Digest::SHA256.new,
  357. real_csrf_token(session),
  358. identifier
  359. )
  360. end
  361. 1 def xor_byte_strings(s1, s2) # :doc:
  362. s2 = s2.dup
  363. size = s1.bytesize
  364. i = 0
  365. while i < size
  366. s2.setbyte(i, s1.getbyte(i) ^ s2.getbyte(i))
  367. i += 1
  368. end
  369. s2
  370. end
  371. # The form's authenticity parameter. Override to provide your own.
  372. 1 def form_authenticity_param # :doc:
  373. params[request_forgery_protection_token]
  374. end
  375. # Checks if the controller allows forgery protection.
  376. 1 def protect_against_forgery? # :doc:
  377. allow_forgery_protection
  378. end
  379. 1 NULL_ORIGIN_MESSAGE = <<~MSG
  380. The browser returned a 'null' origin for a request with origin-based forgery protection turned on. This usually
  381. means you have the 'no-referrer' Referrer-Policy header enabled, or that the request came from a site that
  382. refused to give its origin. This makes it impossible for Rails to verify the source of the requests. Likely the
  383. best solution is to change your referrer policy to something less strict like same-origin or strict-origin.
  384. If you cannot change the referrer policy, you can disable origin checking with the
  385. Rails.application.config.action_controller.forgery_protection_origin_check setting.
  386. MSG
  387. # Checks if the request originated from the same origin by looking at the
  388. # Origin header.
  389. 1 def valid_request_origin? # :doc:
  390. if forgery_protection_origin_check
  391. # We accept blank origin headers because some user agents don't send it.
  392. raise InvalidAuthenticityToken, NULL_ORIGIN_MESSAGE if request.origin == "null"
  393. request.origin.nil? || request.origin == request.base_url
  394. else
  395. true
  396. end
  397. end
  398. 1 def normalize_action_path(action_path) # :doc:
  399. uri = URI.parse(action_path)
  400. uri.path.chomp("/")
  401. end
  402. 1 def generate_csrf_token # :nodoc:
  403. if urlsafe_csrf_tokens
  404. SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH, padding: false)
  405. else
  406. SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
  407. end
  408. end
  409. 1 def encode_csrf_token(csrf_token) # :nodoc:
  410. if urlsafe_csrf_tokens
  411. Base64.urlsafe_encode64(csrf_token, padding: false)
  412. else
  413. Base64.strict_encode64(csrf_token)
  414. end
  415. end
  416. 1 def decode_csrf_token(encoded_csrf_token) # :nodoc:
  417. if urlsafe_csrf_tokens
  418. Base64.urlsafe_decode64(encoded_csrf_token)
  419. else
  420. begin
  421. Base64.strict_decode64(encoded_csrf_token)
  422. rescue ArgumentError
  423. Base64.urlsafe_decode64(encoded_csrf_token)
  424. end
  425. end
  426. end
  427. end
  428. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/rescue.rb

72.73% lines covered

11 relevant lines. 8 lines covered and 3 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController #:nodoc:
  3. # This module is responsible for providing +rescue_from+ helpers
  4. # to controllers and configuring when detailed exceptions must be
  5. # shown.
  6. 1 module Rescue
  7. 1 extend ActiveSupport::Concern
  8. 1 include ActiveSupport::Rescuable
  9. # Override this method if you want to customize when detailed
  10. # exceptions must be shown. This method is only called when
  11. # +consider_all_requests_local+ is +false+. By default, it returns
  12. # +false+, but someone may set it to <tt>request.local?</tt> so local
  13. # requests in production still show the detailed exception pages.
  14. 1 def show_detailed_exceptions?
  15. false
  16. end
  17. 1 private
  18. 1 def process_action(*)
  19. 1 super
  20. rescue Exception => exception
  21. request.env["action_dispatch.show_detailed_exceptions"] ||= show_detailed_exceptions?
  22. rescue_with_handler(exception) || raise
  23. end
  24. end
  25. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/streaming.rb

41.18% lines covered

17 relevant lines. 7 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rack/chunked"
  3. 1 module ActionController #:nodoc:
  4. # Allows views to be streamed back to the client as they are rendered.
  5. #
  6. # By default, Rails renders views by first rendering the template
  7. # and then the layout. The response is sent to the client after the whole
  8. # template is rendered, all queries are made, and the layout is processed.
  9. #
  10. # Streaming inverts the rendering flow by rendering the layout first and
  11. # streaming each part of the layout as they are processed. This allows the
  12. # header of the HTML (which is usually in the layout) to be streamed back
  13. # to client very quickly, allowing JavaScripts and stylesheets to be loaded
  14. # earlier than usual.
  15. #
  16. # This approach was introduced in Rails 3.1 and is still improving. Several
  17. # Rack middlewares may not work and you need to be careful when streaming.
  18. # Those points are going to be addressed soon.
  19. #
  20. # In order to use streaming, you will need to use a Ruby version that
  21. # supports fibers (fibers are supported since version 1.9.2 of the main
  22. # Ruby implementation).
  23. #
  24. # Streaming can be added to a given template easily, all you need to do is
  25. # to pass the :stream option.
  26. #
  27. # class PostsController
  28. # def index
  29. # @posts = Post.all
  30. # render stream: true
  31. # end
  32. # end
  33. #
  34. # == When to use streaming
  35. #
  36. # Streaming may be considered to be overkill for lightweight actions like
  37. # +new+ or +edit+. The real benefit of streaming is on expensive actions
  38. # that, for example, do a lot of queries on the database.
  39. #
  40. # In such actions, you want to delay queries execution as much as you can.
  41. # For example, imagine the following +dashboard+ action:
  42. #
  43. # def dashboard
  44. # @posts = Post.all
  45. # @pages = Page.all
  46. # @articles = Article.all
  47. # end
  48. #
  49. # Most of the queries here are happening in the controller. In order to benefit
  50. # from streaming you would want to rewrite it as:
  51. #
  52. # def dashboard
  53. # # Allow lazy execution of the queries
  54. # @posts = Post.all
  55. # @pages = Page.all
  56. # @articles = Article.all
  57. # render stream: true
  58. # end
  59. #
  60. # Notice that :stream only works with templates. Rendering :json
  61. # or :xml with :stream won't work.
  62. #
  63. # == Communication between layout and template
  64. #
  65. # When streaming, rendering happens top-down instead of inside-out.
  66. # Rails starts with the layout, and the template is rendered later,
  67. # when its +yield+ is reached.
  68. #
  69. # This means that, if your application currently relies on instance
  70. # variables set in the template to be used in the layout, they won't
  71. # work once you move to streaming. The proper way to communicate
  72. # between layout and template, regardless of whether you use streaming
  73. # or not, is by using +content_for+, +provide+ and +yield+.
  74. #
  75. # Take a simple example where the layout expects the template to tell
  76. # which title to use:
  77. #
  78. # <html>
  79. # <head><title><%= yield :title %></title></head>
  80. # <body><%= yield %></body>
  81. # </html>
  82. #
  83. # You would use +content_for+ in your template to specify the title:
  84. #
  85. # <%= content_for :title, "Main" %>
  86. # Hello
  87. #
  88. # And the final result would be:
  89. #
  90. # <html>
  91. # <head><title>Main</title></head>
  92. # <body>Hello</body>
  93. # </html>
  94. #
  95. # However, if +content_for+ is called several times, the final result
  96. # would have all calls concatenated. For instance, if we have the following
  97. # template:
  98. #
  99. # <%= content_for :title, "Main" %>
  100. # Hello
  101. # <%= content_for :title, " page" %>
  102. #
  103. # The final result would be:
  104. #
  105. # <html>
  106. # <head><title>Main page</title></head>
  107. # <body>Hello</body>
  108. # </html>
  109. #
  110. # This means that, if you have <code>yield :title</code> in your layout
  111. # and you want to use streaming, you would have to render the whole template
  112. # (and eventually trigger all queries) before streaming the title and all
  113. # assets, which kills the purpose of streaming. For this purpose, you can use
  114. # a helper called +provide+ that does the same as +content_for+ but tells the
  115. # layout to stop searching for other entries and continue rendering.
  116. #
  117. # For instance, the template above using +provide+ would be:
  118. #
  119. # <%= provide :title, "Main" %>
  120. # Hello
  121. # <%= content_for :title, " page" %>
  122. #
  123. # Giving:
  124. #
  125. # <html>
  126. # <head><title>Main</title></head>
  127. # <body>Hello</body>
  128. # </html>
  129. #
  130. # That said, when streaming, you need to properly check your templates
  131. # and choose when to use +provide+ and +content_for+.
  132. #
  133. # == Headers, cookies, session and flash
  134. #
  135. # When streaming, the HTTP headers are sent to the client right before
  136. # it renders the first line. This means that, modifying headers, cookies,
  137. # session or flash after the template starts rendering will not propagate
  138. # to the client.
  139. #
  140. # == Middlewares
  141. #
  142. # Middlewares that need to manipulate the body won't work with streaming.
  143. # You should disable those middlewares whenever streaming in development
  144. # or production. For instance, <tt>Rack::Bug</tt> won't work when streaming as it
  145. # needs to inject contents in the HTML body.
  146. #
  147. # Also <tt>Rack::Cache</tt> won't work with streaming as it does not support
  148. # streaming bodies yet. Whenever streaming Cache-Control is automatically
  149. # set to "no-cache".
  150. #
  151. # == Errors
  152. #
  153. # When it comes to streaming, exceptions get a bit more complicated. This
  154. # happens because part of the template was already rendered and streamed to
  155. # the client, making it impossible to render a whole exception page.
  156. #
  157. # Currently, when an exception happens in development or production, Rails
  158. # will automatically stream to the client:
  159. #
  160. # "><script>window.location = "/500.html"</script></html>
  161. #
  162. # The first two characters (">) are required in case the exception happens
  163. # while rendering attributes for a given tag. You can check the real cause
  164. # for the exception in your logger.
  165. #
  166. # == Web server support
  167. #
  168. # Not all web servers support streaming out-of-the-box. You need to check
  169. # the instructions for each of them.
  170. #
  171. # ==== Unicorn
  172. #
  173. # Unicorn supports streaming but it needs to be configured. For this, you
  174. # need to create a config file as follow:
  175. #
  176. # # unicorn.config.rb
  177. # listen 3000, tcp_nopush: false
  178. #
  179. # And use it on initialization:
  180. #
  181. # unicorn_rails --config-file unicorn.config.rb
  182. #
  183. # You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
  184. # Please check its documentation for more information: https://bogomips.org/unicorn/Unicorn/Configurator.html#method-i-listen
  185. #
  186. # If you are using Unicorn with NGINX, you may need to tweak NGINX.
  187. # Streaming should work out of the box on Rainbows.
  188. #
  189. # ==== Passenger
  190. #
  191. # To be described.
  192. #
  193. 1 module Streaming
  194. 1 extend ActiveSupport::Concern
  195. 1 private
  196. # Set proper cache control and transfer encoding when streaming
  197. 1 def _process_options(options)
  198. super
  199. if options[:stream]
  200. if request.version == "HTTP/1.0"
  201. options.delete(:stream)
  202. else
  203. headers["Cache-Control"] ||= "no-cache"
  204. headers["Transfer-Encoding"] = "chunked"
  205. headers.delete("Content-Length")
  206. end
  207. end
  208. end
  209. # Call render_body if we are streaming instead of usual +render+.
  210. 1 def _render_template(options)
  211. if options.delete(:stream)
  212. Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
  213. else
  214. super
  215. end
  216. end
  217. end
  218. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/testing.rb

62.5% lines covered

8 relevant lines. 5 lines covered and 3 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 module Testing
  4. 1 extend ActiveSupport::Concern
  5. # Behavior specific to functional tests
  6. 1 module Functional # :nodoc:
  7. 1 def recycle!
  8. @_url_options = nil
  9. self.formats = nil
  10. self.params = nil
  11. end
  12. end
  13. end
  14. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/metal/url_for.rb

29.41% lines covered

17 relevant lines. 5 lines covered and 12 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
  4. # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
  5. #
  6. # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
  7. # URL options like the +host+. In order to do so, this module requires the host class
  8. # to implement +env+ which needs to be Rack-compatible and +request+
  9. # which is either an instance of +ActionDispatch::Request+ or an object
  10. # that responds to the +host+, +optional_port+, +protocol+ and
  11. # +symbolized_path_parameter+ methods.
  12. #
  13. # class RootUrl
  14. # include ActionController::UrlFor
  15. # include Rails.application.routes.url_helpers
  16. #
  17. # delegate :env, :request, to: :controller
  18. #
  19. # def initialize(controller)
  20. # @controller = controller
  21. # @url = root_path # named route from the application.
  22. # end
  23. # end
  24. 1 module UrlFor
  25. 1 extend ActiveSupport::Concern
  26. 1 include AbstractController::UrlFor
  27. 1 def url_options
  28. @_url_options ||= {
  29. host: request.host,
  30. port: request.optional_port,
  31. protocol: request.protocol,
  32. _recall: request.path_parameters
  33. }.merge!(super).freeze
  34. if (same_origin = _routes.equal?(request.routes)) ||
  35. (script_name = request.engine_script_name(_routes)) ||
  36. (original_script_name = request.original_script_name)
  37. options = @_url_options.dup
  38. if original_script_name
  39. options[:original_script_name] = original_script_name
  40. else
  41. if same_origin
  42. options[:script_name] = request.script_name.empty? ? "" : request.script_name.dup
  43. else
  44. options[:script_name] = script_name
  45. end
  46. end
  47. options.freeze
  48. else
  49. @_url_options
  50. end
  51. end
  52. end
  53. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/renderer.rb

77.5% lines covered

40 relevant lines. 31 lines covered and 9 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. # ActionController::Renderer allows you to render arbitrary templates
  4. # without requirement of being in controller actions.
  5. #
  6. # You get a concrete renderer class by invoking ActionController::Base#renderer.
  7. # For example:
  8. #
  9. # ApplicationController.renderer
  10. #
  11. # It allows you to call method #render directly.
  12. #
  13. # ApplicationController.renderer.render template: '...'
  14. #
  15. # You can use this shortcut in a controller, instead of the previous example:
  16. #
  17. # ApplicationController.render template: '...'
  18. #
  19. # #render allows you to use the same options that you can use when rendering in a controller.
  20. # For example:
  21. #
  22. # FooController.render :action, locals: { ... }, assigns: { ... }
  23. #
  24. # The template will be rendered in a Rack environment which is accessible through
  25. # ActionController::Renderer#env. You can set it up in two ways:
  26. #
  27. # * by changing renderer defaults, like
  28. #
  29. # ApplicationController.renderer.defaults # => hash with default Rack environment
  30. #
  31. # * by initializing an instance of renderer by passing it a custom environment.
  32. #
  33. # ApplicationController.renderer.new(method: 'post', https: true)
  34. #
  35. 1 class Renderer
  36. 1 attr_reader :defaults, :controller
  37. DEFAULTS = {
  38. 1 http_host: "example.org",
  39. https: false,
  40. method: "get",
  41. script_name: "",
  42. input: ""
  43. }.freeze
  44. # Create a new renderer instance for a specific controller class.
  45. 1 def self.for(controller, env = {}, defaults = DEFAULTS.dup)
  46. 4 new(controller, env, defaults)
  47. end
  48. # Create a new renderer for the same controller but with a new env.
  49. 1 def new(env = {})
  50. self.class.new controller, env, defaults
  51. end
  52. # Create a new renderer for the same controller but with new defaults.
  53. 1 def with_defaults(defaults)
  54. self.class.new controller, @env, self.defaults.merge(defaults)
  55. end
  56. # Accepts a custom Rack environment to render templates in.
  57. # It will be merged with the default Rack environment defined by
  58. # +ActionController::Renderer::DEFAULTS+.
  59. 1 def initialize(controller, env, defaults)
  60. 4 @controller = controller
  61. 4 @defaults = defaults
  62. 4 @env = normalize_keys defaults, env
  63. end
  64. # Render templates with any options from ActionController::Base#render_to_string.
  65. #
  66. # The primary options are:
  67. # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt> for details.
  68. # * <tt>:file</tt> - Renders an explicit template file. Add <tt>:locals</tt> to pass in, if so desired.
  69. # It shouldn’t be used directly with unsanitized user input due to lack of validation.
  70. # * <tt>:inline</tt> - Renders an ERB template string.
  71. # * <tt>:plain</tt> - Renders provided text and sets the content type as <tt>text/plain</tt>.
  72. # * <tt>:html</tt> - Renders the provided HTML safe string, otherwise
  73. # performs HTML escape on the string first. Sets the content type as <tt>text/html</tt>.
  74. # * <tt>:json</tt> - Renders the provided hash or object in JSON. You don't
  75. # need to call <tt>.to_json</tt> on the object you want to render.
  76. # * <tt>:body</tt> - Renders provided text and sets content type of <tt>text/plain</tt>.
  77. #
  78. # If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, then:
  79. #
  80. # If an object responding to +render_in+ is passed, +render_in+ is called on the object,
  81. # passing in the current view context.
  82. #
  83. # Otherwise, a partial is rendered using the second parameter as the locals hash.
  84. 1 def render(*args)
  85. raise "missing controller" unless controller
  86. request = ActionDispatch::Request.new @env
  87. request.routes = controller._routes
  88. instance = controller.new
  89. instance.set_request! request
  90. instance.set_response! controller.make_response!(request)
  91. instance.render_to_string(*args)
  92. end
  93. 1 alias_method :render_to_string, :render # :nodoc:
  94. 1 private
  95. 1 def normalize_keys(defaults, env)
  96. 4 new_env = {}
  97. 4 env.each_pair { |k, v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
  98. 4 defaults.each_pair do |k, v|
  99. 20 key = rack_key_for(k)
  100. 20 new_env[key] = rack_value_for(k, v) unless new_env.key?(key)
  101. end
  102. 4 new_env["rack.url_scheme"] = new_env["HTTPS"] == "on" ? "https" : "http"
  103. 4 new_env
  104. end
  105. RACK_KEY_TRANSLATION = {
  106. 1 http_host: "HTTP_HOST",
  107. https: "HTTPS",
  108. method: "REQUEST_METHOD",
  109. script_name: "SCRIPT_NAME",
  110. input: "rack.input"
  111. }
  112. 1 def rack_key_for(key)
  113. 20 RACK_KEY_TRANSLATION[key] || key.to_s
  114. end
  115. 1 def rack_value_for(key, value)
  116. 20 case key
  117. when :https
  118. 4 value ? "on" : "off"
  119. when :method
  120. 4 -value.upcase
  121. else
  122. 12 value
  123. end
  124. end
  125. end
  126. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/template_assertions.rb

75.0% lines covered

4 relevant lines. 3 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionController
  3. 1 module TemplateAssertions # :nodoc:
  4. 1 def assert_template(options = {}, message = nil)
  5. raise NoMethodError,
  6. "assert_template has been extracted to a gem. To continue using it,
  7. add `gem 'rails-controller-testing'` to your Gemfile."
  8. end
  9. end
  10. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_controller/test_case.rb

33.99% lines covered

253 relevant lines. 86 lines covered and 167 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rack/session/abstract/id"
  3. 1 require "active_support/core_ext/hash/conversions"
  4. 1 require "active_support/core_ext/object/to_query"
  5. 1 require "active_support/core_ext/module/anonymous"
  6. 1 require "active_support/core_ext/module/redefine_method"
  7. 1 require "active_support/core_ext/hash/keys"
  8. 1 require "active_support/testing/constant_lookup"
  9. 1 require "action_controller/template_assertions"
  10. 1 require "rails-dom-testing"
  11. 1 module ActionController
  12. 1 class Metal
  13. 1 include Testing::Functional
  14. end
  15. 1 module Live
  16. # Disable controller / rendering threads in tests. User tests can access
  17. # the database on the main thread, so they could open a txn, then the
  18. # controller thread will open a new connection and try to access data
  19. # that's only visible to the main thread's txn. This is the problem in #23483.
  20. 1 silence_redefinition_of_method :new_controller_thread
  21. 1 def new_controller_thread # :nodoc:
  22. yield
  23. end
  24. end
  25. # ActionController::TestCase will be deprecated and moved to a gem in the future.
  26. # Please use ActionDispatch::IntegrationTest going forward.
  27. 1 class TestRequest < ActionDispatch::TestRequest #:nodoc:
  28. 1 DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
  29. 1 DEFAULT_ENV.delete "PATH_INFO"
  30. 1 def self.new_session
  31. TestSession.new
  32. end
  33. 1 attr_reader :controller_class
  34. # Create a new test request with default `env` values.
  35. 1 def self.create(controller_class)
  36. env = {}
  37. env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
  38. env["rack.request.cookie_hash"] = {}.with_indifferent_access
  39. new(default_env.merge(env), new_session, controller_class)
  40. end
  41. 1 def self.default_env
  42. DEFAULT_ENV
  43. end
  44. 1 private_class_method :default_env
  45. 1 def initialize(env, session, controller_class)
  46. super(env)
  47. self.session = session
  48. self.session_options = TestSession::DEFAULT_OPTIONS.dup
  49. @controller_class = controller_class
  50. @custom_param_parsers = {
  51. xml: lambda { |raw_post| Hash.from_xml(raw_post)["hash"] }
  52. }
  53. end
  54. 1 def query_string=(string)
  55. set_header Rack::QUERY_STRING, string
  56. end
  57. 1 def content_type=(type)
  58. set_header "CONTENT_TYPE", type
  59. end
  60. 1 def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
  61. non_path_parameters = {}
  62. path_parameters = {}
  63. parameters.each do |key, value|
  64. if query_string_keys.include?(key)
  65. non_path_parameters[key] = value
  66. else
  67. if value.is_a?(Array)
  68. value = value.map(&:to_param)
  69. else
  70. value = value.to_param
  71. end
  72. path_parameters[key.to_sym] = value
  73. end
  74. end
  75. if get?
  76. if query_string.blank?
  77. self.query_string = non_path_parameters.to_query
  78. end
  79. else
  80. if ENCODER.should_multipart?(non_path_parameters)
  81. self.content_type = ENCODER.content_type
  82. data = ENCODER.build_multipart non_path_parameters
  83. else
  84. fetch_header("CONTENT_TYPE") do |k|
  85. set_header k, "application/x-www-form-urlencoded"
  86. end
  87. case content_mime_type.to_sym
  88. when nil
  89. raise "Unknown Content-Type: #{content_type}"
  90. when :json
  91. data = ActiveSupport::JSON.encode(non_path_parameters)
  92. when :xml
  93. data = non_path_parameters.to_xml
  94. when :url_encoded_form
  95. data = non_path_parameters.to_query
  96. else
  97. @custom_param_parsers[content_mime_type.symbol] = ->(_) { non_path_parameters }
  98. data = non_path_parameters.to_query
  99. end
  100. end
  101. data_stream = StringIO.new(data)
  102. set_header "CONTENT_LENGTH", data_stream.length.to_s
  103. set_header "rack.input", data_stream
  104. end
  105. fetch_header("PATH_INFO") do |k|
  106. set_header k, generated_path
  107. end
  108. path_parameters[:controller] = controller_path
  109. path_parameters[:action] = action
  110. self.path_parameters = path_parameters
  111. end
  112. 1 ENCODER = Class.new do
  113. 1 include Rack::Test::Utils
  114. 1 def should_multipart?(params)
  115. # FIXME: lifted from Rack-Test. We should push this separation upstream.
  116. multipart = false
  117. query = lambda { |value|
  118. case value
  119. when Array
  120. value.each(&query)
  121. when Hash
  122. value.values.each(&query)
  123. when Rack::Test::UploadedFile
  124. multipart = true
  125. end
  126. }
  127. params.values.each(&query)
  128. multipart
  129. end
  130. 1 public :build_multipart
  131. 1 def content_type
  132. "multipart/form-data; boundary=#{Rack::Test::MULTIPART_BOUNDARY}"
  133. end
  134. end.new
  135. 1 private
  136. 1 def params_parsers
  137. super.merge @custom_param_parsers
  138. end
  139. end
  140. 1 class LiveTestResponse < Live::Response
  141. # Was the response successful?
  142. 1 alias_method :success?, :successful?
  143. # Was the URL not found?
  144. 1 alias_method :missing?, :not_found?
  145. # Was there a server-side error?
  146. 1 alias_method :error?, :server_error?
  147. end
  148. # Methods #destroy and #load! are overridden to avoid calling methods on the
  149. # @store object, which does not exist for the TestSession class.
  150. 1 class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash #:nodoc:
  151. 1 DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
  152. 1 def initialize(session = {})
  153. super(nil, nil)
  154. @id = Rack::Session::SessionId.new(SecureRandom.hex(16))
  155. @data = stringify_keys(session)
  156. @loaded = true
  157. end
  158. 1 def exists?
  159. true
  160. end
  161. 1 def keys
  162. @data.keys
  163. end
  164. 1 def values
  165. @data.values
  166. end
  167. 1 def destroy
  168. clear
  169. end
  170. 1 def dig(*keys)
  171. keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
  172. @data.dig(*keys)
  173. end
  174. 1 def fetch(key, *args, &block)
  175. @data.fetch(key.to_s, *args, &block)
  176. end
  177. 1 private
  178. 1 def load!
  179. @id
  180. end
  181. end
  182. # Superclass for ActionController functional tests. Functional tests allow you to
  183. # test a single controller action per test method.
  184. #
  185. # == Use integration style controller tests over functional style controller tests.
  186. #
  187. # Rails discourages the use of functional tests in favor of integration tests
  188. # (use ActionDispatch::IntegrationTest).
  189. #
  190. # New Rails applications no longer generate functional style controller tests and they should
  191. # only be used for backward compatibility. Integration style controller tests perform actual
  192. # requests, whereas functional style controller tests merely simulate a request. Besides,
  193. # integration tests are as fast as functional tests and provide lot of helpers such as +as+,
  194. # +parsed_body+ for effective testing of controller actions including even API endpoints.
  195. #
  196. # == Basic example
  197. #
  198. # Functional tests are written as follows:
  199. # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
  200. # an HTTP request.
  201. # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
  202. # the controller's HTTP response, the database contents, etc.
  203. #
  204. # For example:
  205. #
  206. # class BooksControllerTest < ActionController::TestCase
  207. # def test_create
  208. # # Simulate a POST response with the given HTTP parameters.
  209. # post(:create, params: { book: { title: "Love Hina" }})
  210. #
  211. # # Asserts that the controller tried to redirect us to
  212. # # the created book's URI.
  213. # assert_response :found
  214. #
  215. # # Asserts that the controller really put the book in the database.
  216. # assert_not_nil Book.find_by(title: "Love Hina")
  217. # end
  218. # end
  219. #
  220. # You can also send a real document in the simulated HTTP request.
  221. #
  222. # def test_create
  223. # json = {book: { title: "Love Hina" }}.to_json
  224. # post :create, body: json
  225. # end
  226. #
  227. # == Special instance variables
  228. #
  229. # ActionController::TestCase will also automatically provide the following instance
  230. # variables for use in the tests:
  231. #
  232. # <b>@controller</b>::
  233. # The controller instance that will be tested.
  234. # <b>@request</b>::
  235. # An ActionController::TestRequest, representing the current HTTP
  236. # request. You can modify this object before sending the HTTP request. For example,
  237. # you might want to set some session properties before sending a GET request.
  238. # <b>@response</b>::
  239. # An ActionDispatch::TestResponse object, representing the response
  240. # of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
  241. # after calling +post+. If the various assert methods are not sufficient, then you
  242. # may use this object to inspect the HTTP response in detail.
  243. #
  244. # == Controller is automatically inferred
  245. #
  246. # ActionController::TestCase will automatically infer the controller under test
  247. # from the test class name. If the controller cannot be inferred from the test
  248. # class name, you can explicitly set it with +tests+.
  249. #
  250. # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
  251. # tests WidgetController
  252. # end
  253. #
  254. # == \Testing controller internals
  255. #
  256. # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
  257. # can be used against. These collections are:
  258. #
  259. # * session: Objects being saved in the session.
  260. # * flash: The flash objects currently in the session.
  261. # * cookies: \Cookies being sent to the user on this request.
  262. #
  263. # These collections can be used just like any other hash:
  264. #
  265. # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
  266. # assert flash.empty? # makes sure that there's nothing in the flash
  267. #
  268. # On top of the collections, you have the complete URL that a given action redirected to available in <tt>redirect_to_url</tt>.
  269. #
  270. # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
  271. # action call which can then be asserted against.
  272. #
  273. # == Manipulating session and cookie variables
  274. #
  275. # Sometimes you need to set up the session and cookie variables for a test.
  276. # To do this just assign a value to the session or cookie collection:
  277. #
  278. # session[:key] = "value"
  279. # cookies[:key] = "value"
  280. #
  281. # To clear the cookies for a test just clear the cookie collection:
  282. #
  283. # cookies.clear
  284. #
  285. # == \Testing named routes
  286. #
  287. # If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
  288. #
  289. # assert_redirected_to page_url(title: 'foo')
  290. 1 class TestCase < ActiveSupport::TestCase
  291. 1 module Behavior
  292. 1 extend ActiveSupport::Concern
  293. 1 include ActionDispatch::TestProcess
  294. 1 include ActiveSupport::Testing::ConstantLookup
  295. 1 include Rails::Dom::Testing::Assertions
  296. 1 attr_reader :response, :request
  297. 1 module ClassMethods
  298. # Sets the controller class name. Useful if the name can't be inferred from test class.
  299. # Normalizes +controller_class+ before using.
  300. #
  301. # tests WidgetController
  302. # tests :widget
  303. # tests 'widget'
  304. 1 def tests(controller_class)
  305. case controller_class
  306. when String, Symbol
  307. self.controller_class = "#{controller_class.to_s.camelize}Controller".constantize
  308. when Class
  309. self.controller_class = controller_class
  310. else
  311. raise ArgumentError, "controller class must be a String, Symbol, or Class"
  312. end
  313. end
  314. 1 def controller_class=(new_class)
  315. self._controller_class = new_class
  316. end
  317. 1 def controller_class
  318. if current_controller_class = _controller_class
  319. current_controller_class
  320. else
  321. self.controller_class = determine_default_controller_class(name)
  322. end
  323. end
  324. 1 def determine_default_controller_class(name)
  325. determine_constant_from_test_name(name) do |constant|
  326. Class === constant && constant < ActionController::Metal
  327. end
  328. end
  329. end
  330. # Simulate a GET request with the given parameters.
  331. #
  332. # - +action+: The controller action to call.
  333. # - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
  334. # - +body+: The request body with a string that is appropriately encoded
  335. # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
  336. # - +session+: A hash of parameters to store in the session. This may be +nil+.
  337. # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
  338. #
  339. # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
  340. # +post+, +patch+, +put+, +delete+, and +head+.
  341. # Example sending parameters, session and setting a flash message:
  342. #
  343. # get :show,
  344. # params: { id: 7 },
  345. # session: { user_id: 1 },
  346. # flash: { notice: 'This is flash message' }
  347. #
  348. # Note that the request method is not verified. The different methods are
  349. # available to make the tests more expressive.
  350. 1 def get(action, **args)
  351. res = process(action, method: "GET", **args)
  352. cookies.update res.cookies
  353. res
  354. end
  355. # Simulate a POST request with the given parameters and set/volley the response.
  356. # See +get+ for more details.
  357. 1 def post(action, **args)
  358. process(action, method: "POST", **args)
  359. end
  360. # Simulate a PATCH request with the given parameters and set/volley the response.
  361. # See +get+ for more details.
  362. 1 def patch(action, **args)
  363. process(action, method: "PATCH", **args)
  364. end
  365. # Simulate a PUT request with the given parameters and set/volley the response.
  366. # See +get+ for more details.
  367. 1 def put(action, **args)
  368. process(action, method: "PUT", **args)
  369. end
  370. # Simulate a DELETE request with the given parameters and set/volley the response.
  371. # See +get+ for more details.
  372. 1 def delete(action, **args)
  373. process(action, method: "DELETE", **args)
  374. end
  375. # Simulate a HEAD request with the given parameters and set/volley the response.
  376. # See +get+ for more details.
  377. 1 def head(action, **args)
  378. process(action, method: "HEAD", **args)
  379. end
  380. # Simulate an HTTP request to +action+ by specifying request method,
  381. # parameters and set/volley the response.
  382. #
  383. # - +action+: The controller action to call.
  384. # - +method+: Request method used to send the HTTP request. Possible values
  385. # are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+. Can be a symbol.
  386. # - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
  387. # - +body+: The request body with a string that is appropriately encoded
  388. # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
  389. # - +session+: A hash of parameters to store in the session. This may be +nil+.
  390. # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
  391. # - +format+: Request format. Defaults to +nil+. Can be string or symbol.
  392. # - +as+: Content type. Defaults to +nil+. Must be a symbol that corresponds
  393. # to a mime type.
  394. #
  395. # Example calling +create+ action and sending two params:
  396. #
  397. # process :create,
  398. # method: 'POST',
  399. # params: {
  400. # user: { name: 'Gaurish Sharma', email: 'user@example.com' }
  401. # },
  402. # session: { user_id: 1 },
  403. # flash: { notice: 'This is flash message' }
  404. #
  405. # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
  406. # prefer using #get, #post, #patch, #put, #delete and #head methods
  407. # respectively which will make tests more expressive.
  408. #
  409. # Note that the request method is not verified.
  410. 1 def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
  411. check_required_ivars
  412. action = +action.to_s
  413. http_method = method.to_s.upcase
  414. @html_document = nil
  415. cookies.update(@request.cookies)
  416. cookies.update_cookies_from_jar
  417. @request.set_header "HTTP_COOKIE", cookies.to_header
  418. @request.delete_header "action_dispatch.cookies"
  419. @request = TestRequest.new scrub_env!(@request.env), @request.session, @controller.class
  420. @response = build_response @response_klass
  421. @response.request = @request
  422. @controller.recycle!
  423. if body
  424. @request.set_header "RAW_POST_DATA", body
  425. end
  426. @request.set_header "REQUEST_METHOD", http_method
  427. if as
  428. @request.content_type = Mime[as].to_s
  429. format ||= as
  430. end
  431. parameters = (params || {}).symbolize_keys
  432. if format
  433. parameters[:format] = format
  434. end
  435. setup_request(controller_class_name, action, parameters, session, flash, xhr)
  436. process_controller_response(action, cookies, xhr)
  437. end
  438. 1 def controller_class_name
  439. @controller.class.anonymous? ? "anonymous" : @controller.class.controller_path
  440. end
  441. 1 def generated_path(generated_extras)
  442. generated_extras[0]
  443. end
  444. 1 def query_parameter_names(generated_extras)
  445. generated_extras[1] + [:controller, :action]
  446. end
  447. 1 def setup_controller_request_and_response
  448. @controller = nil unless defined? @controller
  449. @response_klass = ActionDispatch::TestResponse
  450. if klass = self.class.controller_class
  451. if klass < ActionController::Live
  452. @response_klass = LiveTestResponse
  453. end
  454. unless @controller
  455. begin
  456. @controller = klass.new
  457. rescue
  458. warn "could not construct controller #{klass}" if $VERBOSE
  459. end
  460. end
  461. end
  462. @request = TestRequest.create(@controller.class)
  463. @response = build_response @response_klass
  464. @response.request = @request
  465. if @controller
  466. @controller.request = @request
  467. @controller.params = {}
  468. end
  469. end
  470. 1 def build_response(klass)
  471. klass.create
  472. end
  473. 1 included do
  474. 1 include ActionController::TemplateAssertions
  475. 1 include ActionDispatch::Assertions
  476. 1 class_attribute :_controller_class
  477. 1 setup :setup_controller_request_and_response
  478. 1 ActiveSupport.run_load_hooks(:action_controller_test_case, self)
  479. end
  480. 1 private
  481. 1 def setup_request(controller_class_name, action, parameters, session, flash, xhr)
  482. generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action))
  483. generated_path = generated_path(generated_extras)
  484. query_string_keys = query_parameter_names(generated_extras)
  485. @request.assign_parameters(@routes, controller_class_name, action, parameters, generated_path, query_string_keys)
  486. @request.session.update(session) if session
  487. @request.flash.update(flash || {})
  488. if xhr
  489. @request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
  490. @request.fetch_header("HTTP_ACCEPT") do |k|
  491. @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
  492. end
  493. end
  494. @request.fetch_header("SCRIPT_NAME") do |k|
  495. @request.set_header k, @controller.config.relative_url_root
  496. end
  497. end
  498. 1 def process_controller_response(action, cookies, xhr)
  499. begin
  500. @controller.recycle!
  501. @controller.dispatch(action, @request, @response)
  502. ensure
  503. @request = @controller.request
  504. @response = @controller.response
  505. if @request.have_cookie_jar?
  506. unless @request.cookie_jar.committed?
  507. @request.cookie_jar.write(@response)
  508. cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
  509. end
  510. end
  511. @response.prepare!
  512. if flash_value = @request.flash.to_session_value
  513. @request.session["flash"] = flash_value
  514. else
  515. @request.session.delete("flash")
  516. end
  517. if xhr
  518. @request.delete_header "HTTP_X_REQUESTED_WITH"
  519. @request.delete_header "HTTP_ACCEPT"
  520. end
  521. @request.query_string = ""
  522. @response.sent!
  523. end
  524. @response
  525. end
  526. 1 def scrub_env!(env)
  527. env.delete_if do |k, _|
  528. k.start_with?("rack.request", "action_dispatch.request", "action_dispatch.rescue")
  529. end
  530. env["rack.input"] = StringIO.new
  531. env.delete "CONTENT_LENGTH"
  532. env.delete "RAW_POST_DATA"
  533. env
  534. end
  535. 1 def document_root_element
  536. html_document.root
  537. end
  538. 1 def check_required_ivars
  539. # Sanity check for required instance variables so we can give an
  540. # understandable error message.
  541. [:@routes, :@controller, :@request, :@response].each do |iv_name|
  542. if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
  543. raise "#{iv_name} is nil: make sure you set it in your test's setup method."
  544. end
  545. end
  546. end
  547. end
  548. 1 include Behavior
  549. end
  550. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/http/filter_redirect.rb

42.11% lines covered

19 relevant lines. 8 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionDispatch
  3. 1 module Http
  4. 1 module FilterRedirect
  5. 1 FILTERED = "[FILTERED]" # :nodoc:
  6. 1 def filtered_location # :nodoc:
  7. if location_filter_match?
  8. FILTERED
  9. else
  10. location
  11. end
  12. end
  13. 1 private
  14. 1 def location_filters
  15. if request
  16. request.get_header("action_dispatch.redirect_filter") || []
  17. else
  18. []
  19. end
  20. end
  21. 1 def location_filter_match?
  22. location_filters.any? do |filter|
  23. if String === filter
  24. location.include?(filter)
  25. elsif Regexp === filter
  26. location.match?(filter)
  27. end
  28. end
  29. end
  30. end
  31. end
  32. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/http/response.rb

77.47% lines covered

253 relevant lines. 196 lines covered and 57 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/module/attribute_accessors"
  3. 1 require "action_dispatch/http/filter_redirect"
  4. 1 require "action_dispatch/http/cache"
  5. 1 require "monitor"
  6. 1 module ActionDispatch # :nodoc:
  7. # Represents an HTTP response generated by a controller action. Use it to
  8. # retrieve the current state of the response, or customize the response. It can
  9. # either represent a real HTTP response (i.e. one that is meant to be sent
  10. # back to the web browser) or a TestResponse (i.e. one that is generated
  11. # from integration tests).
  12. #
  13. # \Response is mostly a Ruby on \Rails framework implementation detail, and
  14. # should never be used directly in controllers. Controllers should use the
  15. # methods defined in ActionController::Base instead. For example, if you want
  16. # to set the HTTP response's content MIME type, then use
  17. # ActionControllerBase#headers instead of Response#headers.
  18. #
  19. # Nevertheless, integration tests may want to inspect controller responses in
  20. # more detail, and that's when \Response can be useful for application
  21. # developers. Integration test methods such as
  22. # ActionDispatch::Integration::Session#get and
  23. # ActionDispatch::Integration::Session#post return objects of type
  24. # TestResponse (which are of course also of type \Response).
  25. #
  26. # For example, the following demo integration test prints the body of the
  27. # controller response to the console:
  28. #
  29. # class DemoControllerTest < ActionDispatch::IntegrationTest
  30. # def test_print_root_path_to_console
  31. # get('/')
  32. # puts response.body
  33. # end
  34. # end
  35. 1 class Response
  36. 1 class Header < DelegateClass(Hash) # :nodoc:
  37. 1 def initialize(response, header)
  38. 3 @response = response
  39. 3 super(header)
  40. end
  41. 1 def []=(k, v)
  42. 1 if @response.sending? || @response.sent?
  43. raise ActionDispatch::IllegalStateError, "header already sent"
  44. end
  45. 1 super
  46. end
  47. 1 def merge(other)
  48. self.class.new @response, __getobj__.merge(other)
  49. end
  50. 1 def to_hash
  51. 1 __getobj__.dup
  52. end
  53. end
  54. # The request that the response is responding to.
  55. 1 attr_accessor :request
  56. # The HTTP status code.
  57. 1 attr_reader :status
  58. # Get headers for this response.
  59. 1 attr_reader :header
  60. 1 alias_method :headers, :header
  61. 1 delegate :[], :[]=, to: :@header
  62. 1 def each(&block)
  63. 1 sending!
  64. 1 x = @stream.each(&block)
  65. 1 sent!
  66. 1 x
  67. end
  68. 1 CONTENT_TYPE = "Content-Type"
  69. 1 SET_COOKIE = "Set-Cookie"
  70. 1 LOCATION = "Location"
  71. 1 NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
  72. 1 cattr_accessor :default_charset, default: "utf-8"
  73. 1 cattr_accessor :default_headers
  74. 1 def self.return_only_media_type_on_content_type=(*)
  75. ActiveSupport::Deprecation.warn(
  76. ".return_only_media_type_on_content_type= is dreprecated with no replacement and will be removed in 6.2."
  77. )
  78. end
  79. 1 def self.return_only_media_type_on_content_type
  80. ActiveSupport::Deprecation.warn(
  81. ".return_only_media_type_on_content_type is dreprecated with no replacement and will be removed in 6.2."
  82. )
  83. end
  84. 1 include Rack::Response::Helpers
  85. # Aliasing these off because AD::Http::Cache::Response defines them.
  86. 1 alias :_cache_control :cache_control
  87. 1 alias :_cache_control= :cache_control=
  88. 1 include ActionDispatch::Http::FilterRedirect
  89. 1 include ActionDispatch::Http::Cache::Response
  90. 1 include MonitorMixin
  91. 1 class Buffer # :nodoc:
  92. 1 def initialize(response, buf)
  93. 5 @response = response
  94. 5 @buf = buf
  95. 5 @closed = false
  96. 5 @str_body = nil
  97. end
  98. 1 def body
  99. 1 @str_body ||= begin
  100. 1 buf = +""
  101. 2 each { |chunk| buf << chunk }
  102. 1 buf
  103. end
  104. end
  105. 1 def write(string)
  106. raise IOError, "closed stream" if closed?
  107. @str_body = nil
  108. @response.commit!
  109. @buf.push string
  110. end
  111. 1 def each(&block)
  112. 2 if @str_body
  113. return enum_for(:each) unless block_given?
  114. yield @str_body
  115. else
  116. 2 each_chunk(&block)
  117. end
  118. end
  119. 1 def abort
  120. end
  121. 1 def close
  122. @response.commit!
  123. @closed = true
  124. end
  125. 1 def closed?
  126. @closed
  127. end
  128. 1 private
  129. 1 def each_chunk(&block)
  130. 2 @buf.each(&block)
  131. end
  132. end
  133. 1 def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
  134. 1 header = merge_default_headers(header, default_headers)
  135. 1 new status, header, body
  136. end
  137. 1 def self.merge_default_headers(original, default)
  138. 1 default.respond_to?(:merge) ? default.merge(original) : original
  139. end
  140. # The underlying body, as a streamable object.
  141. 1 attr_reader :stream
  142. 1 def initialize(status = 200, header = {}, body = [])
  143. 3 super()
  144. 3 @header = Header.new(self, header)
  145. 3 self.body, self.status = body, status
  146. 3 @cv = new_cond
  147. 3 @committed = false
  148. 3 @sending = false
  149. 3 @sent = false
  150. 3 prepare_cache_control!
  151. 3 yield self if block_given?
  152. end
  153. 2 def has_header?(key); headers.key? key; end
  154. 10 def get_header(key); headers[key]; end
  155. 2 def set_header(key, v); headers[key] = v; end
  156. 1 def delete_header(key); headers.delete key; end
  157. 1 def await_commit
  158. synchronize do
  159. @cv.wait_until { @committed }
  160. end
  161. end
  162. 1 def await_sent
  163. synchronize { @cv.wait_until { @sent } }
  164. end
  165. 1 def commit!
  166. 1 synchronize do
  167. 1 before_committed
  168. 1 @committed = true
  169. 1 @cv.broadcast
  170. end
  171. end
  172. 1 def sending!
  173. 1 synchronize do
  174. 1 before_sending
  175. 1 @sending = true
  176. 1 @cv.broadcast
  177. end
  178. end
  179. 1 def sent!
  180. 1 synchronize do
  181. 1 @sent = true
  182. 1 @cv.broadcast
  183. end
  184. end
  185. 3 def sending?; synchronize { @sending }; end
  186. 7 def committed?; synchronize { @committed }; end
  187. 3 def sent?; synchronize { @sent }; end
  188. # Sets the HTTP status code.
  189. 1 def status=(status)
  190. 4 @status = Rack::Utils.status_code(status)
  191. end
  192. # Sets the HTTP response's content MIME type. For example, in the controller
  193. # you could write this:
  194. #
  195. # response.content_type = "text/plain"
  196. #
  197. # If a character set has been defined for this response (see charset=) then
  198. # the character set information will also be included in the content type
  199. # information.
  200. 1 def content_type=(content_type)
  201. 1 return unless content_type
  202. 1 new_header_info = parse_content_type(content_type.to_s)
  203. 1 prev_header_info = parsed_content_type_header
  204. 1 charset = new_header_info.charset || prev_header_info.charset
  205. 1 charset ||= self.class.default_charset unless prev_header_info.mime_type
  206. 1 set_content_type new_header_info.mime_type, charset
  207. end
  208. # Content type of response.
  209. 1 def content_type
  210. super.presence
  211. end
  212. # Media type of response.
  213. 1 def media_type
  214. 3 parsed_content_type_header.mime_type
  215. end
  216. 1 def sending_file=(v)
  217. if true == v
  218. self.charset = false
  219. end
  220. end
  221. # Sets the HTTP character set. In case of +nil+ parameter
  222. # it sets the charset to +default_charset+.
  223. #
  224. # response.charset = 'utf-16' # => 'utf-16'
  225. # response.charset = nil # => 'utf-8'
  226. 1 def charset=(charset)
  227. content_type = parsed_content_type_header.mime_type
  228. if false == charset
  229. set_content_type content_type, nil
  230. else
  231. set_content_type content_type, charset || self.class.default_charset
  232. end
  233. end
  234. # The charset of the response. HTML wants to know the encoding of the
  235. # content you're giving them, so we need to send that along.
  236. 1 def charset
  237. header_info = parsed_content_type_header
  238. header_info.charset || self.class.default_charset
  239. end
  240. # The response code of the request.
  241. 1 def response_code
  242. 1 @status
  243. end
  244. # Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>.
  245. 1 def code
  246. @status.to_s
  247. end
  248. # Returns the corresponding message for the current HTTP status code:
  249. #
  250. # response.status = 200
  251. # response.message # => "OK"
  252. #
  253. # response.status = 404
  254. # response.message # => "Not Found"
  255. #
  256. 1 def message
  257. Rack::Utils::HTTP_STATUS_CODES[@status]
  258. end
  259. 1 alias_method :status_message, :message
  260. # Returns the content of the response as a string. This contains the contents
  261. # of any calls to <tt>render</tt>.
  262. 1 def body
  263. 1 @stream.body
  264. end
  265. 1 def write(string)
  266. @stream.write string
  267. end
  268. # Allows you to manually set or override the response body.
  269. 1 def body=(body)
  270. 4 if body.respond_to?(:to_path)
  271. @stream = body
  272. else
  273. 4 synchronize do
  274. 4 @stream = build_buffer self, munge_body_object(body)
  275. end
  276. end
  277. end
  278. # Avoid having to pass an open file handle as the response body.
  279. # Rack::Sendfile will usually intercept the response and uses
  280. # the path directly, so there is no reason to open the file.
  281. 1 class FileBody #:nodoc:
  282. 1 attr_reader :to_path
  283. 1 def initialize(path)
  284. @to_path = path
  285. end
  286. 1 def body
  287. File.binread(to_path)
  288. end
  289. # Stream the file's contents if Rack::Sendfile isn't present.
  290. 1 def each
  291. File.open(to_path, "rb") do |file|
  292. while chunk = file.read(16384)
  293. yield chunk
  294. end
  295. end
  296. end
  297. end
  298. # Send the file stored at +path+ as the response body.
  299. 1 def send_file(path)
  300. commit!
  301. @stream = FileBody.new(path)
  302. end
  303. 1 def reset_body!
  304. 1 @stream = build_buffer(self, [])
  305. end
  306. 1 def body_parts
  307. parts = []
  308. @stream.each { |x| parts << x }
  309. parts
  310. end
  311. # The location header we'll be responding with.
  312. 1 alias_method :redirect_url, :location
  313. 1 def close
  314. stream.close if stream.respond_to?(:close)
  315. end
  316. 1 def abort
  317. 1 if stream.respond_to?(:abort)
  318. 1 stream.abort
  319. elsif stream.respond_to?(:close)
  320. # `stream.close` should really be reserved for a close from the
  321. # other direction, but we must fall back to it for
  322. # compatibility.
  323. stream.close
  324. end
  325. end
  326. # Turns the Response into a Rack-compatible array of the status, headers,
  327. # and body. Allows explicit splatting:
  328. #
  329. # status, headers, body = *response
  330. 1 def to_a
  331. 1 commit!
  332. 1 rack_response @status, @header.to_hash
  333. end
  334. 1 alias prepare! to_a
  335. # Returns the response cookies, converted to a Hash of (name => value) pairs
  336. #
  337. # assert_equal 'AuthorOfNewPage', r.cookies['author']
  338. 1 def cookies
  339. cookies = {}
  340. if header = get_header(SET_COOKIE)
  341. header = header.split("\n") if header.respond_to?(:to_str)
  342. header.each do |cookie|
  343. if pair = cookie.split(";").first
  344. key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
  345. cookies[key] = value
  346. end
  347. end
  348. end
  349. cookies
  350. end
  351. 1 private
  352. 1 ContentTypeHeader = Struct.new :mime_type, :charset
  353. 1 NullContentTypeHeader = ContentTypeHeader.new nil, nil
  354. 1 CONTENT_TYPE_PARSER = /
  355. \A
  356. (?<mime_type>[^;\s]+\s*(?:;\s*(?:(?!charset)[^;\s])+)*)?
  357. (?:;\s*charset=(?<quote>"?)(?<charset>[^;\s]+)\k<quote>)?
  358. /x # :nodoc:
  359. 1 def parse_content_type(content_type)
  360. 5 if content_type && match = CONTENT_TYPE_PARSER.match(content_type)
  361. 3 ContentTypeHeader.new(match[:mime_type], match[:charset])
  362. else
  363. 2 NullContentTypeHeader
  364. end
  365. end
  366. # Small internal convenience method to get the parsed version of the current
  367. # content type header.
  368. 1 def parsed_content_type_header
  369. 4 parse_content_type(get_header(CONTENT_TYPE))
  370. end
  371. 1 def set_content_type(content_type, charset)
  372. 1 type = content_type || ""
  373. 1 type = "#{type}; charset=#{charset.to_s.downcase}" if charset
  374. 1 set_header CONTENT_TYPE, type
  375. end
  376. 1 def before_committed
  377. 1 return if committed?
  378. 1 assign_default_content_type_and_charset!
  379. 1 merge_and_normalize_cache_control!(@cache_control)
  380. 1 handle_conditional_get!
  381. 1 handle_no_content!
  382. end
  383. 1 def before_sending
  384. # Normally we've already committed by now, but it's possible
  385. # (e.g., if the controller action tries to read back its own
  386. # response) to get here before that. In that case, we must force
  387. # an "early" commit: we're about to freeze the headers, so this is
  388. # our last chance.
  389. 1 commit! unless committed?
  390. 1 headers.freeze
  391. 1 request.commit_cookie_jar! unless committed?
  392. end
  393. 1 def build_buffer(response, body)
  394. 5 Buffer.new response, body
  395. end
  396. 1 def munge_body_object(body)
  397. 4 body.respond_to?(:each) ? body : [body]
  398. end
  399. 1 def assign_default_content_type_and_charset!
  400. 1 return if media_type
  401. ct = parsed_content_type_header
  402. set_content_type(ct.mime_type || Mime[:html].to_s,
  403. ct.charset || self.class.default_charset)
  404. end
  405. 1 class RackBody
  406. 1 def initialize(response)
  407. 1 @response = response
  408. end
  409. 1 def each(*args, &block)
  410. 1 @response.each(*args, &block)
  411. end
  412. 1 def close
  413. # Rack "close" maps to Response#abort, and *not* Response#close
  414. # (which is used when the controller's finished writing)
  415. 1 @response.abort
  416. end
  417. 1 def body
  418. @response.body
  419. end
  420. 1 def respond_to?(method, include_private = false)
  421. 2 if method.to_sym == :to_path
  422. 1 @response.stream.respond_to?(method)
  423. else
  424. 1 super
  425. end
  426. end
  427. 1 def to_path
  428. @response.stream.to_path
  429. end
  430. 1 def to_ary
  431. nil
  432. end
  433. end
  434. 1 def handle_no_content!
  435. 1 if NO_CONTENT_CODES.include?(@status)
  436. @header.delete CONTENT_TYPE
  437. @header.delete "Content-Length"
  438. end
  439. end
  440. 1 def rack_response(status, header)
  441. 1 if NO_CONTENT_CODES.include?(status)
  442. [status, header, []]
  443. else
  444. 1 [status, header, RackBody.new(self)]
  445. end
  446. end
  447. end
  448. 1 ActiveSupport.run_load_hooks(:action_dispatch_response, Response)
  449. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/middleware/flash.rb

42.64% lines covered

129 relevant lines. 55 lines covered and 74 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/hash/keys"
  3. 1 module ActionDispatch
  4. # The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
  5. # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
  6. # action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
  7. # then expose the flash to its template. Actually, that exposure is automatically done.
  8. #
  9. # class PostsController < ActionController::Base
  10. # def create
  11. # # save post
  12. # flash[:notice] = "Post successfully created"
  13. # redirect_to @post
  14. # end
  15. #
  16. # def show
  17. # # doesn't need to assign the flash notice to the template, that's done automatically
  18. # end
  19. # end
  20. #
  21. # show.html.erb
  22. # <% if flash[:notice] %>
  23. # <div class="notice"><%= flash[:notice] %></div>
  24. # <% end %>
  25. #
  26. # Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
  27. #
  28. # flash.alert = "You must be logged in"
  29. # flash.notice = "Post successfully created"
  30. #
  31. # This example places a string in the flash. And of course, you can put as many as you like at a time too. If you want to pass
  32. # non-primitive types, you will have to handle that in your application. Example: To show messages with links, you will have to
  33. # use sanitize helper.
  34. #
  35. # Just remember: They'll be gone by the time the next action has been performed.
  36. #
  37. # See docs on the FlashHash class for more details about the flash.
  38. 1 class Flash
  39. 1 KEY = "action_dispatch.request.flash_hash"
  40. 1 module RequestMethods
  41. # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
  42. # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
  43. # to put a new one.
  44. 1 def flash
  45. flash = flash_hash
  46. return flash if flash
  47. self.flash = Flash::FlashHash.from_session_value(session["flash"])
  48. end
  49. 1 def flash=(flash)
  50. set_header Flash::KEY, flash
  51. end
  52. 1 def flash_hash # :nodoc:
  53. 1 get_header Flash::KEY
  54. end
  55. 1 def commit_flash # :nodoc:
  56. 1 session = self.session || {}
  57. 1 flash_hash = self.flash_hash
  58. 1 if flash_hash && (flash_hash.present? || session.key?("flash"))
  59. session["flash"] = flash_hash.to_session_value
  60. self.flash = flash_hash.dup
  61. end
  62. 1 if (!session.respond_to?(:loaded?) || session.loaded?) && # reset_session uses {}, which doesn't implement #loaded?
  63. session.key?("flash") && session["flash"].nil?
  64. session.delete("flash")
  65. end
  66. end
  67. 1 def reset_session # :nodoc:
  68. super
  69. self.flash = nil
  70. end
  71. end
  72. 1 class FlashNow #:nodoc:
  73. 1 attr_accessor :flash
  74. 1 def initialize(flash)
  75. @flash = flash
  76. end
  77. 1 def []=(k, v)
  78. k = k.to_s
  79. @flash[k] = v
  80. @flash.discard(k)
  81. v
  82. end
  83. 1 def [](k)
  84. @flash[k.to_s]
  85. end
  86. # Convenience accessor for <tt>flash.now[:alert]=</tt>.
  87. 1 def alert=(message)
  88. self[:alert] = message
  89. end
  90. # Convenience accessor for <tt>flash.now[:notice]=</tt>.
  91. 1 def notice=(message)
  92. self[:notice] = message
  93. end
  94. end
  95. 1 class FlashHash
  96. 1 include Enumerable
  97. 1 def self.from_session_value(value) #:nodoc:
  98. case value
  99. when FlashHash # Rails 3.1, 3.2
  100. flashes = value.instance_variable_get(:@flashes)
  101. if discard = value.instance_variable_get(:@used)
  102. flashes.except!(*discard)
  103. end
  104. new(flashes, flashes.keys)
  105. when Hash # Rails 4.0
  106. flashes = value["flashes"]
  107. if discard = value["discard"]
  108. flashes.except!(*discard)
  109. end
  110. new(flashes, flashes.keys)
  111. else
  112. new
  113. end
  114. end
  115. # Builds a hash containing the flashes to keep for the next request.
  116. # If there are none to keep, returns +nil+.
  117. 1 def to_session_value #:nodoc:
  118. flashes_to_keep = @flashes.except(*@discard)
  119. return nil if flashes_to_keep.empty?
  120. { "discard" => [], "flashes" => flashes_to_keep }
  121. end
  122. 1 def initialize(flashes = {}, discard = []) #:nodoc:
  123. @discard = Set.new(stringify_array(discard))
  124. @flashes = flashes.stringify_keys
  125. @now = nil
  126. end
  127. 1 def initialize_copy(other)
  128. if other.now_is_loaded?
  129. @now = other.now.dup
  130. @now.flash = self
  131. end
  132. super
  133. end
  134. 1 def []=(k, v)
  135. k = k.to_s
  136. @discard.delete k
  137. @flashes[k] = v
  138. end
  139. 1 def [](k)
  140. @flashes[k.to_s]
  141. end
  142. 1 def update(h) #:nodoc:
  143. @discard.subtract stringify_array(h.keys)
  144. @flashes.update h.stringify_keys
  145. self
  146. end
  147. 1 def keys
  148. @flashes.keys
  149. end
  150. 1 def key?(name)
  151. @flashes.key? name.to_s
  152. end
  153. 1 def delete(key)
  154. key = key.to_s
  155. @discard.delete key
  156. @flashes.delete key
  157. self
  158. end
  159. 1 def to_hash
  160. @flashes.dup
  161. end
  162. 1 def empty?
  163. @flashes.empty?
  164. end
  165. 1 def clear
  166. @discard.clear
  167. @flashes.clear
  168. end
  169. 1 def each(&block)
  170. @flashes.each(&block)
  171. end
  172. 1 alias :merge! :update
  173. 1 def replace(h) #:nodoc:
  174. @discard.clear
  175. @flashes.replace h.stringify_keys
  176. self
  177. end
  178. # Sets a flash that will not be available to the next action, only to the current.
  179. #
  180. # flash.now[:message] = "Hello current action"
  181. #
  182. # This method enables you to use the flash as a central messaging system in your app.
  183. # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
  184. # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
  185. # vanish when the current action is done.
  186. #
  187. # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
  188. #
  189. # Also, brings two convenience accessors:
  190. #
  191. # flash.now.alert = "Beware now!"
  192. # # Equivalent to flash.now[:alert] = "Beware now!"
  193. #
  194. # flash.now.notice = "Good luck now!"
  195. # # Equivalent to flash.now[:notice] = "Good luck now!"
  196. 1 def now
  197. @now ||= FlashNow.new(self)
  198. end
  199. # Keeps either the entire current flash or a specific flash entry available for the next action:
  200. #
  201. # flash.keep # keeps the entire flash
  202. # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
  203. 1 def keep(k = nil)
  204. k = k.to_s if k
  205. @discard.subtract Array(k || keys)
  206. k ? self[k] : self
  207. end
  208. # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
  209. #
  210. # flash.discard # discard the entire flash at the end of the current action
  211. # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
  212. 1 def discard(k = nil)
  213. k = k.to_s if k
  214. @discard.merge Array(k || keys)
  215. k ? self[k] : self
  216. end
  217. # Mark for removal entries that were kept, and delete unkept ones.
  218. #
  219. # This method is called automatically by filters, so you generally don't need to care about it.
  220. 1 def sweep #:nodoc:
  221. @discard.each { |k| @flashes.delete k }
  222. @discard.replace @flashes.keys
  223. end
  224. # Convenience accessor for <tt>flash[:alert]</tt>.
  225. 1 def alert
  226. self[:alert]
  227. end
  228. # Convenience accessor for <tt>flash[:alert]=</tt>.
  229. 1 def alert=(message)
  230. self[:alert] = message
  231. end
  232. # Convenience accessor for <tt>flash[:notice]</tt>.
  233. 1 def notice
  234. self[:notice]
  235. end
  236. # Convenience accessor for <tt>flash[:notice]=</tt>.
  237. 1 def notice=(message)
  238. self[:notice] = message
  239. end
  240. 1 protected
  241. 1 def now_is_loaded?
  242. @now
  243. end
  244. 1 private
  245. 1 def stringify_array(array) # :doc:
  246. array.map do |item|
  247. item.kind_of?(Symbol) ? item.to_s : item
  248. end
  249. end
  250. end
  251. 1 def self.new(app) app; end
  252. end
  253. 1 class Request
  254. 1 prepend Flash::RequestMethods
  255. end
  256. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions.rb

76.92% lines covered

13 relevant lines. 10 lines covered and 3 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rails-dom-testing"
  3. 1 module ActionDispatch
  4. 1 module Assertions
  5. 1 autoload :ResponseAssertions, "action_dispatch/testing/assertions/response"
  6. 1 autoload :RoutingAssertions, "action_dispatch/testing/assertions/routing"
  7. 1 extend ActiveSupport::Concern
  8. 1 include ResponseAssertions
  9. 1 include RoutingAssertions
  10. 1 include Rails::Dom::Testing::Assertions
  11. 1 def html_document
  12. @html_document ||= if @response.media_type&.end_with?("xml")
  13. Nokogiri::XML::Document.parse(@response.body)
  14. else
  15. Nokogiri::HTML::Document.parse(@response.body)
  16. end
  17. end
  18. end
  19. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions/response.rb

35.14% lines covered

37 relevant lines. 13 lines covered and 24 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionDispatch
  3. 1 module Assertions
  4. # A small suite of assertions that test responses from \Rails applications.
  5. 1 module ResponseAssertions
  6. RESPONSE_PREDICATES = { # :nodoc:
  7. 1 success: :successful?,
  8. missing: :not_found?,
  9. redirect: :redirection?,
  10. error: :server_error?,
  11. }
  12. # Asserts that the response is one of the following types:
  13. #
  14. # * <tt>:success</tt> - Status code was in the 200-299 range
  15. # * <tt>:redirect</tt> - Status code was in the 300-399 range
  16. # * <tt>:missing</tt> - Status code was 404
  17. # * <tt>:error</tt> - Status code was in the 500-599 range
  18. #
  19. # You can also pass an explicit status number like <tt>assert_response(501)</tt>
  20. # or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
  21. # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
  22. #
  23. # # Asserts that the response was a redirection
  24. # assert_response :redirect
  25. #
  26. # # Asserts that the response code was status code 401 (unauthorized)
  27. # assert_response 401
  28. 1 def assert_response(type, message = nil)
  29. message ||= generate_response_message(type)
  30. if RESPONSE_PREDICATES.keys.include?(type)
  31. assert @response.public_send(RESPONSE_PREDICATES[type]), message
  32. else
  33. assert_equal AssertionResponse.new(type).code, @response.response_code, message
  34. end
  35. end
  36. # Asserts that the response is a redirect to a URL matching the given options.
  37. #
  38. # # Asserts that the redirection was to the "index" action on the WeblogController
  39. # assert_redirected_to controller: "weblog", action: "index"
  40. #
  41. # # Asserts that the redirection was to the named route login_url
  42. # assert_redirected_to login_url
  43. #
  44. # # Asserts that the redirection was to the URL for @customer
  45. # assert_redirected_to @customer
  46. #
  47. # # Asserts that the redirection matches the regular expression
  48. # assert_redirected_to %r(\Ahttp://example.org)
  49. 1 def assert_redirected_to(options = {}, message = nil)
  50. assert_response(:redirect, message)
  51. return true if options === @response.location
  52. redirect_is = normalize_argument_to_redirection(@response.location)
  53. redirect_expected = normalize_argument_to_redirection(options)
  54. message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
  55. assert_operator redirect_expected, :===, redirect_is, message
  56. end
  57. 1 private
  58. # Proxy to to_param if the object will respond to it.
  59. 1 def parameterize(value)
  60. value.respond_to?(:to_param) ? value.to_param : value
  61. end
  62. 1 def normalize_argument_to_redirection(fragment)
  63. if Regexp === fragment
  64. fragment
  65. else
  66. handle = @controller || ActionController::Redirecting
  67. handle._compute_redirect_to_location(@request, fragment)
  68. end
  69. end
  70. 1 def generate_response_message(expected, actual = @response.response_code)
  71. (+"Expected response to be a <#{code_with_name(expected)}>,"\
  72. " but was a <#{code_with_name(actual)}>").concat(location_if_redirected).concat(response_body_if_short)
  73. end
  74. 1 def response_body_if_short
  75. return "" if @response.body.size > 500
  76. "\nResponse body: #{@response.body}"
  77. end
  78. 1 def location_if_redirected
  79. return "" unless @response.redirection? && @response.location.present?
  80. location = normalize_argument_to_redirection(@response.location)
  81. " redirect to <#{location}>"
  82. end
  83. 1 def code_with_name(code_or_name)
  84. if RESPONSE_PREDICATES.values.include?("#{code_or_name}?".to_sym)
  85. code_or_name = RESPONSE_PREDICATES.invert["#{code_or_name}?".to_sym]
  86. end
  87. AssertionResponse.new(code_or_name).code_and_name
  88. end
  89. end
  90. end
  91. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/assertions/routing.rb

18.6% lines covered

86 relevant lines. 16 lines covered and 70 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "uri"
  3. 1 require "active_support/core_ext/hash/indifferent_access"
  4. 1 require "active_support/core_ext/string/access"
  5. 1 require "action_controller/metal/exceptions"
  6. 1 module ActionDispatch
  7. 1 module Assertions
  8. # Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
  9. 1 module RoutingAssertions
  10. 1 def setup # :nodoc:
  11. @routes ||= nil
  12. super
  13. end
  14. # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
  15. # match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
  16. #
  17. # Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
  18. # requiring a specific HTTP method. The hash should contain a :path with the incoming request path
  19. # and a :method containing the required HTTP verb.
  20. #
  21. # # Asserts that POSTing to /items will call the create action on ItemsController
  22. # assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
  23. #
  24. # You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
  25. # to assert that values in the query string will end up in the params hash correctly. To test query strings you must use the extras
  26. # argument because appending the query string on the path directly will not work. For example:
  27. #
  28. # # Asserts that a path of '/items/list/1?view=print' returns the correct options
  29. # assert_recognizes({controller: 'items', action: 'list', id: '1', view: 'print'}, 'items/list/1', { view: "print" })
  30. #
  31. # The +message+ parameter allows you to pass in an error message that is displayed upon failure.
  32. #
  33. # # Check the default route (i.e., the index action)
  34. # assert_recognizes({controller: 'items', action: 'index'}, 'items')
  35. #
  36. # # Test a specific action
  37. # assert_recognizes({controller: 'items', action: 'list'}, 'items/list')
  38. #
  39. # # Test an action with a parameter
  40. # assert_recognizes({controller: 'items', action: 'destroy', id: '1'}, 'items/destroy/1')
  41. #
  42. # # Test a custom route
  43. # assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
  44. 1 def assert_recognizes(expected_options, path, extras = {}, msg = nil)
  45. if path.is_a?(Hash) && path[:method].to_s == "all"
  46. [:get, :post, :put, :delete].each do |method|
  47. assert_recognizes(expected_options, path.merge(method: method), extras, msg)
  48. end
  49. else
  50. request = recognized_request_for(path, extras, msg)
  51. expected_options = expected_options.clone
  52. expected_options.stringify_keys!
  53. msg = message(msg, "") {
  54. sprintf("The recognized options <%s> did not match <%s>, difference:",
  55. request.path_parameters, expected_options)
  56. }
  57. assert_equal(expected_options, request.path_parameters, msg)
  58. end
  59. end
  60. # Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
  61. # The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
  62. # a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
  63. #
  64. # The +defaults+ parameter is unused.
  65. #
  66. # # Asserts that the default action is generated for a route with no action
  67. # assert_generates "/items", controller: "items", action: "index"
  68. #
  69. # # Tests that the list action is properly routed
  70. # assert_generates "/items/list", controller: "items", action: "list"
  71. #
  72. # # Tests the generation of a route with a parameter
  73. # assert_generates "/items/list/1", { controller: "items", action: "list", id: "1" }
  74. #
  75. # # Asserts that the generated route gives us our custom route
  76. # assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
  77. 1 def assert_generates(expected_path, options, defaults = {}, extras = {}, message = nil)
  78. if %r{://}.match?(expected_path)
  79. fail_on(URI::InvalidURIError, message) do
  80. uri = URI.parse(expected_path)
  81. expected_path = uri.path.to_s.empty? ? "/" : uri.path
  82. end
  83. else
  84. expected_path = "/#{expected_path}" unless expected_path.start_with?("/")
  85. end
  86. options = options.clone
  87. generated_path, query_string_keys = @routes.generate_extras(options, defaults)
  88. found_extras = options.reject { |k, _| ! query_string_keys.include? k }
  89. msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
  90. assert_equal(extras, found_extras, msg)
  91. msg = message || sprintf("The generated path <%s> did not match <%s>", generated_path,
  92. expected_path)
  93. assert_equal(expected_path, generated_path, msg)
  94. end
  95. # Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
  96. # <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+
  97. # and +assert_generates+ into one step.
  98. #
  99. # The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
  100. # +message+ parameter allows you to specify a custom error message to display upon failure.
  101. #
  102. # # Asserts a basic route: a controller with the default action (index)
  103. # assert_routing '/home', controller: 'home', action: 'index'
  104. #
  105. # # Test a route generated with a specific controller, action, and parameter (id)
  106. # assert_routing '/entries/show/23', controller: 'entries', action: 'show', id: 23
  107. #
  108. # # Asserts a basic route (controller + default action), with an error message if it fails
  109. # assert_routing '/store', { controller: 'store', action: 'index' }, {}, {}, 'Route for store index not generated properly'
  110. #
  111. # # Tests a route, providing a defaults hash
  112. # assert_routing 'controller/action/9', {id: "9", item: "square"}, {controller: "controller", action: "action"}, {}, {item: "square"}
  113. #
  114. # # Tests a route with an HTTP method
  115. # assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
  116. 1 def assert_routing(path, options, defaults = {}, extras = {}, message = nil)
  117. assert_recognizes(options, path, extras, message)
  118. controller, default_controller = options[:controller], defaults[:controller]
  119. if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
  120. options[:controller] = "/#{controller}"
  121. end
  122. generate_options = options.dup.delete_if { |k, _| defaults.key?(k) }
  123. assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
  124. end
  125. # A helper to make it easier to test different route configurations.
  126. # This method temporarily replaces @routes with a new RouteSet instance.
  127. #
  128. # The new instance is yielded to the passed block. Typically the block
  129. # will create some routes using <tt>set.draw { match ... }</tt>:
  130. #
  131. # with_routing do |set|
  132. # set.draw do
  133. # resources :users
  134. # end
  135. # assert_equal "/users", users_path
  136. # end
  137. #
  138. 1 def with_routing
  139. old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
  140. if defined?(@controller) && @controller
  141. old_controller, @controller = @controller, @controller.clone
  142. _routes = @routes
  143. @controller.singleton_class.include(_routes.url_helpers)
  144. if @controller.respond_to? :view_context_class
  145. view_context_class = Class.new(@controller.view_context_class) do
  146. include _routes.url_helpers
  147. end
  148. custom_view_context = Module.new {
  149. define_method(:view_context_class) do
  150. view_context_class
  151. end
  152. }
  153. @controller.extend(custom_view_context)
  154. end
  155. end
  156. yield @routes
  157. ensure
  158. @routes = old_routes
  159. if defined?(@controller) && @controller
  160. @controller = old_controller
  161. end
  162. end
  163. # ROUTES TODO: These assertions should really work in an integration context
  164. 1 def method_missing(selector, *args, &block)
  165. if defined?(@controller) && @controller && defined?(@routes) && @routes && @routes.named_routes.route_defined?(selector)
  166. @controller.public_send(selector, *args, &block)
  167. else
  168. super
  169. end
  170. end
  171. 1 private
  172. # Recognizes the route for a given path.
  173. 1 def recognized_request_for(path, extras = {}, msg)
  174. if path.is_a?(Hash)
  175. method = path[:method]
  176. path = path[:path]
  177. else
  178. method = :get
  179. end
  180. controller = @controller if defined?(@controller)
  181. request = ActionController::TestRequest.create controller&.class
  182. if %r{://}.match?(path)
  183. fail_on(URI::InvalidURIError, msg) do
  184. uri = URI.parse(path)
  185. request.env["rack.url_scheme"] = uri.scheme || "http"
  186. request.host = uri.host if uri.host
  187. request.port = uri.port if uri.port
  188. request.path = uri.path.to_s.empty? ? "/" : uri.path
  189. end
  190. else
  191. path = "/#{path}" unless path.start_with?("/")
  192. request.path = path
  193. end
  194. request.request_method = method if method
  195. params = fail_on(ActionController::RoutingError, msg) do
  196. @routes.recognize_path(path, method: method, extras: extras)
  197. end
  198. request.path_parameters = params.with_indifferent_access
  199. request
  200. end
  201. 1 def fail_on(exception_class, message)
  202. yield
  203. rescue exception_class => e
  204. raise Minitest::Assertion, message || e.message
  205. end
  206. end
  207. end
  208. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/integration.rb

73.53% lines covered

204 relevant lines. 150 lines covered and 54 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "stringio"
  3. 1 require "uri"
  4. 1 require "rack/test"
  5. 1 require "minitest"
  6. 1 require "action_dispatch/testing/request_encoder"
  7. 1 module ActionDispatch
  8. 1 module Integration #:nodoc:
  9. 1 module RequestHelpers
  10. # Performs a GET request with the given parameters. See ActionDispatch::Integration::Session#process
  11. # for more details.
  12. 1 def get(path, **args)
  13. 2 process(:get, path, **args)
  14. end
  15. # Performs a POST request with the given parameters. See ActionDispatch::Integration::Session#process
  16. # for more details.
  17. 1 def post(path, **args)
  18. process(:post, path, **args)
  19. end
  20. # Performs a PATCH request with the given parameters. See ActionDispatch::Integration::Session#process
  21. # for more details.
  22. 1 def patch(path, **args)
  23. process(:patch, path, **args)
  24. end
  25. # Performs a PUT request with the given parameters. See ActionDispatch::Integration::Session#process
  26. # for more details.
  27. 1 def put(path, **args)
  28. process(:put, path, **args)
  29. end
  30. # Performs a DELETE request with the given parameters. See ActionDispatch::Integration::Session#process
  31. # for more details.
  32. 1 def delete(path, **args)
  33. process(:delete, path, **args)
  34. end
  35. # Performs a HEAD request with the given parameters. See ActionDispatch::Integration::Session#process
  36. # for more details.
  37. 1 def head(path, **args)
  38. process(:head, path, **args)
  39. end
  40. # Performs an OPTIONS request with the given parameters. See ActionDispatch::Integration::Session#process
  41. # for more details.
  42. 1 def options(path, **args)
  43. process(:options, path, **args)
  44. end
  45. # Follow a single redirect response. If the last response was not a
  46. # redirect, an exception will be raised. Otherwise, the redirect is
  47. # performed on the location header. If the redirection is a 307 or 308 redirect,
  48. # the same HTTP verb will be used when redirecting, otherwise a GET request
  49. # will be performed. Any arguments are passed to the
  50. # underlying request.
  51. 1 def follow_redirect!(**args)
  52. raise "not a redirect! #{status} #{status_message}" unless redirect?
  53. method =
  54. if [307, 308].include?(response.status)
  55. request.method.downcase
  56. else
  57. :get
  58. end
  59. public_send(method, response.location, **args)
  60. status
  61. end
  62. end
  63. # An instance of this class represents a set of requests and responses
  64. # performed sequentially by a test process. Because you can instantiate
  65. # multiple sessions and run them side-by-side, you can also mimic (to some
  66. # limited extent) multiple simultaneous users interacting with your system.
  67. #
  68. # Typically, you will instantiate a new session using
  69. # IntegrationTest#open_session, rather than instantiating
  70. # Integration::Session directly.
  71. 1 class Session
  72. 1 DEFAULT_HOST = "www.example.com"
  73. 1 include Minitest::Assertions
  74. 1 include TestProcess, RequestHelpers, Assertions
  75. 1 delegate :status, :status_message, :headers, :body, :redirect?, to: :response, allow_nil: true
  76. 1 delegate :path, to: :request, allow_nil: true
  77. # The hostname used in the last request.
  78. 1 def host
  79. 6 @host || DEFAULT_HOST
  80. end
  81. 1 attr_writer :host
  82. # The remote_addr used in the last request.
  83. 1 attr_accessor :remote_addr
  84. # The Accept header to send.
  85. 1 attr_accessor :accept
  86. # A map of the cookies returned by the last response, and which will be
  87. # sent with the next request.
  88. 1 def cookies
  89. _mock_session.cookie_jar
  90. end
  91. # A reference to the controller instance used by the last request.
  92. 1 attr_reader :controller
  93. # A reference to the request instance used by the last request.
  94. 1 attr_reader :request
  95. # A reference to the response instance used by the last request.
  96. 1 attr_reader :response
  97. # A running counter of the number of requests processed.
  98. 1 attr_accessor :request_count
  99. 1 include ActionDispatch::Routing::UrlFor
  100. # Create and initialize a new Session instance.
  101. 1 def initialize(app)
  102. 2 super()
  103. 2 @app = app
  104. 2 reset!
  105. end
  106. 1 def url_options
  107. @url_options ||= default_url_options.dup.tap do |url_options|
  108. url_options.reverse_merge!(controller.url_options) if controller.respond_to?(:url_options)
  109. if @app.respond_to?(:routes)
  110. url_options.reverse_merge!(@app.routes.default_url_options)
  111. end
  112. url_options.reverse_merge!(host: host, protocol: https? ? "https" : "http")
  113. end
  114. end
  115. # Resets the instance. This can be used to reset the state information
  116. # in an existing session instance, so it can be used from a clean-slate
  117. # condition.
  118. #
  119. # session.reset!
  120. 1 def reset!
  121. 2 @https = false
  122. 2 @controller = @request = @response = nil
  123. 2 @_mock_session = nil
  124. 2 @request_count = 0
  125. 2 @url_options = nil
  126. 2 self.host = DEFAULT_HOST
  127. 2 self.remote_addr = "127.0.0.1"
  128. 2 self.accept = "text/xml,application/xml,application/xhtml+xml," \
  129. "text/html;q=0.9,text/plain;q=0.8,image/png," \
  130. "*/*;q=0.5"
  131. 2 unless defined? @named_routes_configured
  132. # the helpers are made protected by default--we make them public for
  133. # easier access during testing and troubleshooting.
  134. 2 @named_routes_configured = true
  135. end
  136. end
  137. # Specify whether or not the session should mimic a secure HTTPS request.
  138. #
  139. # session.https!
  140. # session.https!(false)
  141. 1 def https!(flag = true)
  142. @https = flag
  143. end
  144. # Returns +true+ if the session is mimicking a secure HTTPS request.
  145. #
  146. # if session.https?
  147. # ...
  148. # end
  149. 1 def https?
  150. 6 @https
  151. end
  152. # Performs the actual request.
  153. #
  154. # - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
  155. # as a symbol.
  156. # - +path+: The URI (as a String) on which you want to perform the
  157. # request.
  158. # - +params+: The HTTP parameters that you want to pass. This may
  159. # be +nil+,
  160. # a Hash, or a String that is appropriately encoded
  161. # (<tt>application/x-www-form-urlencoded</tt> or
  162. # <tt>multipart/form-data</tt>).
  163. # - +headers+: Additional headers to pass, as a Hash. The headers will be
  164. # merged into the Rack env hash.
  165. # - +env+: Additional env to pass, as a Hash. The headers will be
  166. # merged into the Rack env hash.
  167. # - +xhr+: Set to +true+ if you want to make and Ajax request.
  168. # Adds request headers characteristic of XMLHttpRequest e.g. HTTP_X_REQUESTED_WITH.
  169. # The headers will be merged into the Rack env hash.
  170. # - +as+: Used for encoding the request with different content type.
  171. # Supports +:json+ by default and will set the appropriate request headers.
  172. # The headers will be merged into the Rack env hash.
  173. #
  174. # This method is rarely used directly. Use +#get+, +#post+, or other standard
  175. # HTTP methods in integration tests. +#process+ is only required when using a
  176. # request method that doesn't have a method defined in the integration tests.
  177. #
  178. # This method returns the response status, after performing the request.
  179. # Furthermore, if this method was called from an ActionDispatch::IntegrationTest object,
  180. # then that object's <tt>@response</tt> instance variable will point to a Response object
  181. # which one can use to inspect the details of the response.
  182. #
  183. # Example:
  184. # process :get, '/author', params: { since: 201501011400 }
  185. 1 def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
  186. 2 request_encoder = RequestEncoder.encoder(as)
  187. 2 headers ||= {}
  188. 2 if method == :get && as == :json && params
  189. headers["X-Http-Method-Override"] = "GET"
  190. method = :post
  191. end
  192. 2 if %r{://}.match?(path)
  193. path = build_expanded_path(path) do |location|
  194. https! URI::HTTPS === location if location.scheme
  195. if url_host = location.host
  196. default = Rack::Request::DEFAULT_PORTS[location.scheme]
  197. url_host += ":#{location.port}" if default != location.port
  198. host! url_host
  199. end
  200. end
  201. end
  202. 2 hostname, port = host.split(":")
  203. request_env = {
  204. 2 :method => method,
  205. :params => request_encoder.encode_params(params),
  206. "SERVER_NAME" => hostname,
  207. 2 "SERVER_PORT" => port || (https? ? "443" : "80"),
  208. 2 "HTTPS" => https? ? "on" : "off",
  209. 2 "rack.url_scheme" => https? ? "https" : "http",
  210. "REQUEST_URI" => path,
  211. "HTTP_HOST" => host,
  212. "REMOTE_ADDR" => remote_addr,
  213. "CONTENT_TYPE" => request_encoder.content_type,
  214. "HTTP_ACCEPT" => request_encoder.accept_header || accept
  215. }
  216. 2 wrapped_headers = Http::Headers.from_hash({})
  217. 2 wrapped_headers.merge!(headers) if headers
  218. 2 if xhr
  219. wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
  220. wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
  221. end
  222. # This modifies the passed request_env directly.
  223. 2 if wrapped_headers.present?
  224. 2 Http::Headers.from_hash(request_env).merge!(wrapped_headers)
  225. end
  226. 2 if env.present?
  227. Http::Headers.from_hash(request_env).merge!(env)
  228. end
  229. 2 session = Rack::Test::Session.new(_mock_session)
  230. # NOTE: rack-test v0.5 doesn't build a default uri correctly
  231. # Make sure requested path is always a full URI.
  232. 2 session.request(build_full_uri(path, request_env), request_env)
  233. 1 @request_count += 1
  234. 1 @request = ActionDispatch::Request.new(session.last_request.env)
  235. 1 response = _mock_session.last_response
  236. 1 @response = ActionDispatch::TestResponse.from_response(response)
  237. 1 @response.request = @request
  238. 1 @html_document = nil
  239. 1 @url_options = nil
  240. 1 @controller = @request.controller_instance
  241. 1 response.status
  242. end
  243. # Set the host name to use in the next request.
  244. #
  245. # session.host! "www.example.com"
  246. 1 alias :host! :host=
  247. 1 private
  248. 1 def _mock_session
  249. 3 @_mock_session ||= Rack::MockSession.new(@app, host)
  250. end
  251. 1 def build_full_uri(path, env)
  252. 2 "#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
  253. end
  254. 1 def build_expanded_path(path)
  255. location = URI.parse(path)
  256. yield location if block_given?
  257. path = location.path
  258. location.query ? "#{path}?#{location.query}" : path
  259. end
  260. end
  261. 1 module Runner
  262. 1 include ActionDispatch::Assertions
  263. 1 APP_SESSIONS = {}
  264. 1 attr_reader :app
  265. 1 attr_accessor :root_session # :nodoc:
  266. 1 def initialize(*args, &blk)
  267. 13 super(*args, &blk)
  268. 13 @integration_session = nil
  269. end
  270. 1 def before_setup # :nodoc:
  271. 3 @app = nil
  272. 3 super
  273. end
  274. 1 def integration_session
  275. 4 @integration_session ||= create_session(app)
  276. end
  277. # Reset the current session. This is useful for testing multiple sessions
  278. # in a single test case.
  279. 1 def reset!
  280. @integration_session = create_session(app)
  281. end
  282. 1 def create_session(app)
  283. 2 klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
  284. # If the app is a Rails app, make url_helpers available on the session.
  285. # This makes app.url_for and app.foo_path available in the console.
  286. 1 if app.respond_to?(:routes) && app.routes.is_a?(ActionDispatch::Routing::RouteSet)
  287. 1 include app.routes.url_helpers
  288. 1 include app.routes.mounted_helpers
  289. end
  290. }
  291. 2 klass.new(app)
  292. end
  293. 1 def remove! # :nodoc:
  294. @integration_session = nil
  295. end
  296. 1 %w(get post patch put head delete cookies assigns follow_redirect!).each do |method|
  297. # reset the html_document variable, except for cookies/assigns calls
  298. 9 unless method == "cookies" || method == "assigns"
  299. 7 reset_html_document = "@html_document = nil"
  300. end
  301. 9 definition = RUBY_VERSION >= "2.7" ? "..." : "*args"
  302. 9 module_eval <<~RUBY, __FILE__, __LINE__ + 1
  303. def #{method}(#{definition})
  304. #{reset_html_document}
  305. result = integration_session.#{method}(#{definition})
  306. copy_session_variables!
  307. result
  308. end
  309. RUBY
  310. end
  311. # Open a new session instance. If a block is given, the new session is
  312. # yielded to the block before being returned.
  313. #
  314. # session = open_session do |sess|
  315. # sess.extend(CustomAssertions)
  316. # end
  317. #
  318. # By default, a single session is automatically created for you, but you
  319. # can use this method to open multiple sessions that ought to be tested
  320. # simultaneously.
  321. 1 def open_session
  322. dup.tap do |session|
  323. session.reset!
  324. session.root_session = self.root_session || self
  325. yield session if block_given?
  326. end
  327. end
  328. 1 def assertions # :nodoc:
  329. root_session ? root_session.assertions : super
  330. end
  331. 1 def assertions=(assertions) # :nodoc:
  332. root_session ? root_session.assertions = assertions : super
  333. end
  334. # Copy the instance variables from the current session instance into the
  335. # test instance.
  336. 1 def copy_session_variables! #:nodoc:
  337. 2 @controller = @integration_session.controller
  338. 2 @response = @integration_session.response
  339. 2 @request = @integration_session.request
  340. end
  341. 1 def default_url_options
  342. integration_session.default_url_options
  343. end
  344. 1 def default_url_options=(options)
  345. integration_session.default_url_options = options
  346. end
  347. 1 private
  348. 1 def respond_to_missing?(method, _)
  349. integration_session.respond_to?(method) || super
  350. end
  351. # Delegate unhandled messages to the current session instance.
  352. 1 def method_missing(method, *args, &block)
  353. 1 if integration_session.respond_to?(method)
  354. 1 integration_session.public_send(method, *args, &block).tap do
  355. 1 copy_session_variables!
  356. end
  357. else
  358. super
  359. end
  360. end
  361. 1 ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
  362. end
  363. end
  364. # An integration test spans multiple controllers and actions,
  365. # tying them all together to ensure they work together as expected. It tests
  366. # more completely than either unit or functional tests do, exercising the
  367. # entire stack, from the dispatcher to the database.
  368. #
  369. # At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
  370. # using the get/post methods:
  371. #
  372. # require "test_helper"
  373. #
  374. # class ExampleTest < ActionDispatch::IntegrationTest
  375. # fixtures :people
  376. #
  377. # def test_login
  378. # # get the login page
  379. # get "/login"
  380. # assert_equal 200, status
  381. #
  382. # # post the login and follow through to the home page
  383. # post "/login", params: { username: people(:jamis).username,
  384. # password: people(:jamis).password }
  385. # follow_redirect!
  386. # assert_equal 200, status
  387. # assert_equal "/home", path
  388. # end
  389. # end
  390. #
  391. # However, you can also have multiple session instances open per test, and
  392. # even extend those instances with assertions and methods to create a very
  393. # powerful testing DSL that is specific for your application. You can even
  394. # reference any named routes you happen to have defined.
  395. #
  396. # require "test_helper"
  397. #
  398. # class AdvancedTest < ActionDispatch::IntegrationTest
  399. # fixtures :people, :rooms
  400. #
  401. # def test_login_and_speak
  402. # jamis, david = login(:jamis), login(:david)
  403. # room = rooms(:office)
  404. #
  405. # jamis.enter(room)
  406. # jamis.speak(room, "anybody home?")
  407. #
  408. # david.enter(room)
  409. # david.speak(room, "hello!")
  410. # end
  411. #
  412. # private
  413. #
  414. # module CustomAssertions
  415. # def enter(room)
  416. # # reference a named route, for maximum internal consistency!
  417. # get(room_url(id: room.id))
  418. # assert(...)
  419. # ...
  420. # end
  421. #
  422. # def speak(room, message)
  423. # post "/say/#{room.id}", xhr: true, params: { message: message }
  424. # assert(...)
  425. # ...
  426. # end
  427. # end
  428. #
  429. # def login(who)
  430. # open_session do |sess|
  431. # sess.extend(CustomAssertions)
  432. # who = people(who)
  433. # sess.post "/login", params: { username: who.username,
  434. # password: who.password }
  435. # assert(...)
  436. # end
  437. # end
  438. # end
  439. #
  440. # Another longer example would be:
  441. #
  442. # A simple integration test that exercises multiple controllers:
  443. #
  444. # require "test_helper"
  445. #
  446. # class UserFlowsTest < ActionDispatch::IntegrationTest
  447. # test "login and browse site" do
  448. # # login via https
  449. # https!
  450. # get "/login"
  451. # assert_response :success
  452. #
  453. # post "/login", params: { username: users(:david).username, password: users(:david).password }
  454. # follow_redirect!
  455. # assert_equal '/welcome', path
  456. # assert_equal 'Welcome david!', flash[:notice]
  457. #
  458. # https!(false)
  459. # get "/articles/all"
  460. # assert_response :success
  461. # assert_select 'h1', 'Articles'
  462. # end
  463. # end
  464. #
  465. # As you can see the integration test involves multiple controllers and
  466. # exercises the entire stack from database to dispatcher. In addition you can
  467. # have multiple session instances open simultaneously in a test and extend
  468. # those instances with assertion methods to create a very powerful testing
  469. # DSL (domain-specific language) just for your application.
  470. #
  471. # Here's an example of multiple sessions and custom DSL in an integration test
  472. #
  473. # require "test_helper"
  474. #
  475. # class UserFlowsTest < ActionDispatch::IntegrationTest
  476. # test "login and browse site" do
  477. # # User david logs in
  478. # david = login(:david)
  479. # # User guest logs in
  480. # guest = login(:guest)
  481. #
  482. # # Both are now available in different sessions
  483. # assert_equal 'Welcome david!', david.flash[:notice]
  484. # assert_equal 'Welcome guest!', guest.flash[:notice]
  485. #
  486. # # User david can browse site
  487. # david.browses_site
  488. # # User guest can browse site as well
  489. # guest.browses_site
  490. #
  491. # # Continue with other assertions
  492. # end
  493. #
  494. # private
  495. #
  496. # module CustomDsl
  497. # def browses_site
  498. # get "/products/all"
  499. # assert_response :success
  500. # assert_select 'h1', 'Products'
  501. # end
  502. # end
  503. #
  504. # def login(user)
  505. # open_session do |sess|
  506. # sess.extend(CustomDsl)
  507. # u = users(user)
  508. # sess.https!
  509. # sess.post "/login", params: { username: u.username, password: u.password }
  510. # assert_equal '/welcome', sess.path
  511. # sess.https!(false)
  512. # end
  513. # end
  514. # end
  515. #
  516. # See the {request helpers documentation}[rdoc-ref:ActionDispatch::Integration::RequestHelpers] for help on how to
  517. # use +get+, etc.
  518. #
  519. # === Changing the request encoding
  520. #
  521. # You can also test your JSON API easily by setting what the request should
  522. # be encoded as:
  523. #
  524. # require "test_helper"
  525. #
  526. # class ApiTest < ActionDispatch::IntegrationTest
  527. # test "creates articles" do
  528. # assert_difference -> { Article.count } do
  529. # post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
  530. # end
  531. #
  532. # assert_response :success
  533. # assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
  534. # end
  535. # end
  536. #
  537. # The +as+ option passes an "application/json" Accept header (thereby setting
  538. # the request format to JSON unless overridden), sets the content type to
  539. # "application/json" and encodes the parameters as JSON.
  540. #
  541. # Calling +parsed_body+ on the response parses the response body based on the
  542. # last response MIME type.
  543. #
  544. # Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
  545. # types you've registered, you can add your own encoders with:
  546. #
  547. # ActionDispatch::IntegrationTest.register_encoder :wibble,
  548. # param_encoder: -> params { params.to_wibble },
  549. # response_parser: -> body { body }
  550. #
  551. # Where +param_encoder+ defines how the params should be encoded and
  552. # +response_parser+ defines how the response body should be parsed through
  553. # +parsed_body+.
  554. #
  555. # Consult the Rails Testing Guide for more.
  556. 1 class IntegrationTest < ActiveSupport::TestCase
  557. 1 include TestProcess::FixtureFile
  558. 1 module UrlOptions
  559. 1 extend ActiveSupport::Concern
  560. 1 def url_options
  561. integration_session.url_options
  562. end
  563. end
  564. 1 module Behavior
  565. 1 extend ActiveSupport::Concern
  566. 1 include Integration::Runner
  567. 1 include ActionController::TemplateAssertions
  568. 1 included do
  569. 5 include ActionDispatch::Routing::UrlFor
  570. 5 include UrlOptions # don't let UrlFor override the url_options method
  571. 5 ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
  572. 5 @@app = nil
  573. end
  574. 1 module ClassMethods
  575. 1 def app
  576. if defined?(@@app) && @@app
  577. @@app
  578. else
  579. ActionDispatch.test_app
  580. end
  581. end
  582. 1 def app=(app)
  583. @@app = app
  584. end
  585. 1 def register_encoder(*args, **options)
  586. RequestEncoder.register_encoder(*args, **options)
  587. end
  588. end
  589. 1 def app
  590. super || self.class.app
  591. end
  592. 1 def document_root_element
  593. html_document.root
  594. end
  595. end
  596. 1 include Behavior
  597. end
  598. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/request_encoder.rb

79.31% lines covered

29 relevant lines. 23 lines covered and 6 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionDispatch
  3. 1 class RequestEncoder # :nodoc:
  4. 1 class IdentityEncoder
  5. 1 def content_type; end
  6. 1 def accept_header; end
  7. 3 def encode_params(params); params; end
  8. 1 def response_parser; -> body { body }; end
  9. end
  10. 1 @encoders = { identity: IdentityEncoder.new }
  11. 1 attr_reader :response_parser
  12. 1 def initialize(mime_name, param_encoder, response_parser)
  13. 1 @mime = Mime[mime_name]
  14. 1 unless @mime
  15. raise ArgumentError, "Can't register a request encoder for " \
  16. "unregistered MIME Type: #{mime_name}. See `Mime::Type.register`."
  17. end
  18. 1 @response_parser = response_parser || -> body { body }
  19. 1 @param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
  20. end
  21. 1 def content_type
  22. @mime.to_s
  23. end
  24. 1 def accept_header
  25. @mime.to_s
  26. end
  27. 1 def encode_params(params)
  28. @param_encoder.call(params) if params
  29. end
  30. 1 def self.parser(content_type)
  31. type = Mime::Type.lookup(content_type).ref if content_type
  32. encoder(type).response_parser
  33. end
  34. 1 def self.encoder(name)
  35. 2 @encoders[name] || @encoders[:identity]
  36. end
  37. 1 def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
  38. 1 @encoders[mime_name] = new(mime_name, param_encoder, response_parser)
  39. end
  40. 1 register_encoder :json, response_parser: -> body { JSON.parse(body) }
  41. end
  42. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_process.rb

41.38% lines covered

29 relevant lines. 12 lines covered and 17 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_dispatch/middleware/cookies"
  3. 1 require "action_dispatch/middleware/flash"
  4. 1 module ActionDispatch
  5. 1 module TestProcess
  6. 1 module FixtureFile
  7. # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.file_fixture_path, path), type)</tt>:
  8. #
  9. # post :change_avatar, params: { avatar: fixture_file_upload('spongebob.png', 'image/png') }
  10. #
  11. # Default fixture files location is <tt>test/fixtures/files</tt>.
  12. #
  13. # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
  14. # This will not affect other platforms:
  15. #
  16. # post :change_avatar, params: { avatar: fixture_file_upload('spongebob.png', 'image/png', :binary) }
  17. 1 def fixture_file_upload(path, mime_type = nil, binary = false)
  18. if self.class.respond_to?(:fixture_path) && self.class.fixture_path &&
  19. !File.exist?(path)
  20. original_path = path
  21. path = Pathname.new(self.class.fixture_path).join(path)
  22. if !self.class.file_fixture_path
  23. ActiveSupport::Deprecation.warn(<<~EOM)
  24. Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
  25. In Rails 6.2, the path needs to be relative to `file_fixture_path` which you
  26. haven't set yet. Set `file_fixture_path` to discard this warning.
  27. EOM
  28. elsif path.exist?
  29. non_deprecated_path = Pathname(File.absolute_path(path)).relative_path_from(Pathname(File.absolute_path(self.class.file_fixture_path)))
  30. ActiveSupport::Deprecation.warn(<<~EOM)
  31. Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
  32. In Rails 6.2, the path needs to be relative to `file_fixture_path`.
  33. Please modify the call from
  34. `fixture_file_upload("#{original_path}")` to `fixture_file_upload("#{non_deprecated_path}")`.
  35. EOM
  36. else
  37. path = file_fixture(original_path)
  38. end
  39. elsif self.class.file_fixture_path && !File.exist?(path)
  40. path = file_fixture(path)
  41. end
  42. Rack::Test::UploadedFile.new(path, mime_type, binary)
  43. end
  44. end
  45. 1 include FixtureFile
  46. 1 def assigns(key = nil)
  47. raise NoMethodError,
  48. "assigns has been extracted to a gem. To continue using it,
  49. add `gem 'rails-controller-testing'` to your Gemfile."
  50. end
  51. 1 def session
  52. @request.session
  53. end
  54. 1 def flash
  55. @request.flash
  56. end
  57. 1 def cookies
  58. @cookie_jar ||= Cookies::CookieJar.build(@request, @request.cookies)
  59. end
  60. 1 def redirect_to_url
  61. @response.redirect_url
  62. end
  63. end
  64. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_request.rb

54.29% lines covered

35 relevant lines. 19 lines covered and 16 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/hash/indifferent_access"
  3. 1 require "rack/utils"
  4. 1 module ActionDispatch
  5. 1 class TestRequest < Request
  6. 1 DEFAULT_ENV = Rack::MockRequest.env_for("/",
  7. "HTTP_HOST" => "test.host".b,
  8. "REMOTE_ADDR" => "0.0.0.0".b,
  9. "HTTP_USER_AGENT" => "Rails Testing".b,
  10. )
  11. # Create a new test request with default +env+ values.
  12. 1 def self.create(env = {})
  13. env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
  14. env["rack.request.cookie_hash"] ||= {}.with_indifferent_access
  15. new(default_env.merge(env))
  16. end
  17. 1 def self.default_env
  18. DEFAULT_ENV
  19. end
  20. 1 private_class_method :default_env
  21. 1 def request_method=(method)
  22. super(method.to_s.upcase)
  23. end
  24. 1 def host=(host)
  25. set_header("HTTP_HOST", host)
  26. end
  27. 1 def port=(number)
  28. set_header("SERVER_PORT", number.to_i)
  29. end
  30. 1 def request_uri=(uri)
  31. set_header("REQUEST_URI", uri)
  32. end
  33. 1 def path=(path)
  34. set_header("PATH_INFO", path)
  35. end
  36. 1 def action=(action_name)
  37. path_parameters[:action] = action_name.to_s
  38. end
  39. 1 def if_modified_since=(last_modified)
  40. set_header("HTTP_IF_MODIFIED_SINCE", last_modified)
  41. end
  42. 1 def if_none_match=(etag)
  43. set_header("HTTP_IF_NONE_MATCH", etag)
  44. end
  45. 1 def remote_addr=(addr)
  46. set_header("REMOTE_ADDR", addr)
  47. end
  48. 1 def user_agent=(user_agent)
  49. set_header("HTTP_USER_AGENT", user_agent)
  50. end
  51. 1 def accept=(mime_types)
  52. delete_header("action_dispatch.request.accepts")
  53. set_header("HTTP_ACCEPT", Array(mime_types).collect(&:to_s).join(","))
  54. end
  55. end
  56. end

vender/bundle/ruby/2.6.0/gems/actionpack-6.1.3/lib/action_dispatch/testing/test_response.rb

77.78% lines covered

9 relevant lines. 7 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_dispatch/testing/request_encoder"
  3. 1 module ActionDispatch
  4. # Integration test methods such as ActionDispatch::Integration::Session#get
  5. # and ActionDispatch::Integration::Session#post return objects of class
  6. # TestResponse, which represent the HTTP response results of the requested
  7. # controller actions.
  8. #
  9. # See Response for more information on controller response objects.
  10. 1 class TestResponse < Response
  11. 1 def self.from_response(response)
  12. 2 new response.status, response.headers, response.body
  13. end
  14. 1 def parsed_body
  15. @parsed_body ||= response_parser.call(body)
  16. end
  17. 1 def response_parser
  18. @response_parser ||= RequestEncoder.parser(media_type)
  19. end
  20. end
  21. end

vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/app/helpers/action_text/content_helper.rb

47.62% lines covered

21 relevant lines. 10 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rails-html-sanitizer"
  3. 1 module ActionText
  4. 1 module ContentHelper
  5. 2 mattr_accessor(:sanitizer) { Rails::Html::Sanitizer.safe_list_sanitizer.new }
  6. 2 mattr_accessor(:allowed_tags) { sanitizer.class.allowed_tags + [ ActionText::Attachment::TAG_NAME, "figure", "figcaption" ] }
  7. 2 mattr_accessor(:allowed_attributes) { sanitizer.class.allowed_attributes + ActionText::Attachment::ATTRIBUTES }
  8. 1 mattr_accessor(:scrubber)
  9. 1 def render_action_text_content(content)
  10. self.prefix_partial_path_with_controller_namespace = false
  11. sanitize_action_text_content(render_action_text_attachments(content))
  12. end
  13. 1 def sanitize_action_text_content(content)
  14. sanitizer.sanitize(content.to_html, tags: allowed_tags, attributes: allowed_attributes, scrubber: scrubber).html_safe
  15. end
  16. 1 def render_action_text_attachments(content)
  17. content.render_attachments do |attachment|
  18. unless attachment.in?(content.gallery_attachments)
  19. attachment.node.tap do |node|
  20. node.inner_html = render(attachment, in_gallery: false).chomp
  21. end
  22. end
  23. end.render_attachment_galleries do |attachment_gallery|
  24. render(layout: attachment_gallery, object: attachment_gallery) do
  25. attachment_gallery.attachments.map do |attachment|
  26. attachment.node.inner_html = render(attachment, in_gallery: true).chomp
  27. attachment.to_html
  28. end.join.html_safe
  29. end.chomp
  30. end
  31. end
  32. end
  33. end

vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/app/helpers/action_text/tag_helper.rb

50.0% lines covered

32 relevant lines. 16 lines covered and 16 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/object/try"
  3. 1 require "action_view/helpers/tags/placeholderable"
  4. 1 module ActionText
  5. 1 module TagHelper
  6. 2 cattr_accessor(:id, instance_accessor: false) { 0 }
  7. # Returns a +trix-editor+ tag that instantiates the Trix JavaScript editor as well as a hidden field
  8. # that Trix will write to on changes, so the content will be sent on form submissions.
  9. #
  10. # ==== Options
  11. # * <tt>:class</tt> - Defaults to "trix-content" which ensures default styling is applied.
  12. #
  13. # ==== Example
  14. #
  15. # rich_text_area_tag "content", message.content
  16. # # <input type="hidden" name="content" id="trix_input_post_1">
  17. # # <trix-editor id="content" input="trix_input_post_1" class="trix-content" ...></trix-editor>
  18. 1 def rich_text_area_tag(name, value = nil, options = {})
  19. options = options.symbolize_keys
  20. options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}"
  21. options[:class] ||= "trix-content"
  22. options[:data] ||= {}
  23. options[:data][:direct_upload_url] = main_app.rails_direct_uploads_url
  24. options[:data][:blob_url_template] = main_app.rails_service_blob_url(":signed_id", ":filename")
  25. editor_tag = content_tag("trix-editor", "", options)
  26. input_tag = hidden_field_tag(name, value, id: options[:input])
  27. input_tag + editor_tag
  28. end
  29. end
  30. end
  31. 1 module ActionView::Helpers
  32. 1 class Tags::ActionText < Tags::Base
  33. 1 include Tags::Placeholderable
  34. 1 delegate :dom_id, to: ActionView::RecordIdentifier
  35. 1 def render
  36. options = @options.stringify_keys
  37. add_default_name_and_id(options)
  38. options["input"] ||= dom_id(object, [options["id"], :trix_input].compact.join("_")) if object
  39. @template_object.rich_text_area_tag(options.delete("name"), options.fetch("value") { editable_value }, options.except("value"))
  40. end
  41. 1 def editable_value
  42. value&.body.try(:to_trix_html)
  43. end
  44. end
  45. 1 module FormHelper
  46. # Returns a +trix-editor+ tag that instantiates the Trix JavaScript editor as well as a hidden field
  47. # that Trix will write to on changes, so the content will be sent on form submissions.
  48. #
  49. # ==== Options
  50. # * <tt>:class</tt> - Defaults to "trix-content" which ensures default styling is applied.
  51. # * <tt>:value</tt> - Adds a default value to the HTML input tag.
  52. #
  53. # ==== Example
  54. # form_with(model: @message) do |form|
  55. # form.rich_text_area :content
  56. # end
  57. # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1">
  58. # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
  59. #
  60. # form_with(model: @message) do |form|
  61. # form.rich_text_area :content, value: "<h1>Default message</h1>"
  62. # end
  63. # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1" value="<h1>Default message</h1>">
  64. # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
  65. 1 def rich_text_area(object_name, method, options = {})
  66. Tags::ActionText.new(object_name, method, self, options).render
  67. end
  68. end
  69. 1 class FormBuilder
  70. 1 def rich_text_area(method, options = {})
  71. @template.rich_text_area(@object_name, method, objectify_options(options))
  72. end
  73. end
  74. end

vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachment.rb

56.36% lines covered

55 relevant lines. 31 lines covered and 24 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/object/try"
  3. 1 module ActionText
  4. 1 class Attachment
  5. 1 include Attachments::TrixConversion, Attachments::Minification, Attachments::Caching
  6. 1 TAG_NAME = "action-text-attachment"
  7. 1 SELECTOR = TAG_NAME
  8. 1 ATTRIBUTES = %w( sgid content-type url href filename filesize width height previewable presentation caption )
  9. 1 class << self
  10. 1 def fragment_by_canonicalizing_attachments(content)
  11. fragment_by_minifying_attachments(fragment_by_converting_trix_attachments(content))
  12. end
  13. 1 def from_node(node, attachable = nil)
  14. new(node, attachable || ActionText::Attachable.from_node(node))
  15. end
  16. 1 def from_attachables(attachables)
  17. Array(attachables).map { |attachable| from_attachable(attachable) }.compact
  18. end
  19. 1 def from_attachable(attachable, attributes = {})
  20. if node = node_from_attributes(attachable.to_rich_text_attributes(attributes))
  21. new(node, attachable)
  22. end
  23. end
  24. 1 def from_attributes(attributes, attachable = nil)
  25. if node = node_from_attributes(attributes)
  26. from_node(node, attachable)
  27. end
  28. end
  29. 1 private
  30. 1 def node_from_attributes(attributes)
  31. if attributes = process_attributes(attributes).presence
  32. ActionText::HtmlConversion.create_element(TAG_NAME, attributes)
  33. end
  34. end
  35. 1 def process_attributes(attributes)
  36. attributes.transform_keys { |key| key.to_s.underscore.dasherize }.slice(*ATTRIBUTES)
  37. end
  38. end
  39. 1 attr_reader :node, :attachable
  40. 1 delegate :to_param, to: :attachable
  41. 1 delegate_missing_to :attachable
  42. 1 def initialize(node, attachable)
  43. @node = node
  44. @attachable = attachable
  45. end
  46. 1 def caption
  47. node_attributes["caption"].presence
  48. end
  49. 1 def full_attributes
  50. node_attributes.merge(attachable_attributes).merge(sgid_attributes)
  51. end
  52. 1 def with_full_attributes
  53. self.class.from_attributes(full_attributes, attachable)
  54. end
  55. 1 def to_plain_text
  56. if respond_to?(:attachable_plain_text_representation)
  57. attachable_plain_text_representation(caption)
  58. else
  59. caption.to_s
  60. end
  61. end
  62. 1 def to_html
  63. HtmlConversion.node_to_html(node)
  64. end
  65. 1 def to_s
  66. to_html
  67. end
  68. 1 def inspect
  69. "#<#{self.class.name} attachable=#{attachable.inspect}>"
  70. end
  71. 1 private
  72. 1 def node_attributes
  73. @node_attributes ||= ATTRIBUTES.map { |name| [ name.underscore, node[name] ] }.to_h.compact
  74. end
  75. 1 def attachable_attributes
  76. @attachable_attributes ||= (attachable.try(:to_rich_text_attributes) || {}).stringify_keys
  77. end
  78. 1 def sgid_attributes
  79. @sgid_attributes ||= node_attributes.slice("sgid").presence || attachable_attributes.slice("sgid")
  80. end
  81. end
  82. end

vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/caching.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionText
  3. 1 module Attachments
  4. 1 module Caching
  5. 1 def cache_key(*args)
  6. [self.class.name, cache_digest, *attachable.cache_key(*args)].join("/")
  7. end
  8. 1 private
  9. 1 def cache_digest
  10. Digest::SHA256.hexdigest(node.to_s)
  11. end
  12. end
  13. end
  14. end

vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/minification.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionText
  3. 1 module Attachments
  4. 1 module Minification
  5. 1 extend ActiveSupport::Concern
  6. 1 class_methods do
  7. 1 def fragment_by_minifying_attachments(content)
  8. Fragment.wrap(content).replace(ActionText::Attachment::SELECTOR) do |node|
  9. node.tap { |n| n.inner_html = "" }
  10. end
  11. end
  12. end
  13. end
  14. end
  15. end

vender/bundle/ruby/2.6.0/gems/actiontext-6.1.3/lib/action_text/attachments/trix_conversion.rb

57.89% lines covered

19 relevant lines. 11 lines covered and 8 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/object/try"
  3. 1 module ActionText
  4. 1 module Attachments
  5. 1 module TrixConversion
  6. 1 extend ActiveSupport::Concern
  7. 1 class_methods do
  8. 1 def fragment_by_converting_trix_attachments(content)
  9. Fragment.wrap(content).replace(TrixAttachment::SELECTOR) do |node|
  10. from_trix_attachment(TrixAttachment.new(node))
  11. end
  12. end
  13. 1 def from_trix_attachment(trix_attachment)
  14. from_attributes(trix_attachment.attributes)
  15. end
  16. end
  17. 1 def to_trix_attachment(content = trix_attachment_content)
  18. attributes = full_attributes.dup
  19. attributes["content"] = content if content
  20. TrixAttachment.from_attributes(attributes)
  21. end
  22. 1 private
  23. 1 def trix_attachment_content
  24. if partial_path = attachable.try(:to_trix_content_attachment_partial_path)
  25. ActionText::Content.render(partial: partial_path, object: self, as: model_name.element)
  26. end
  27. end
  28. end
  29. end
  30. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/base.rb

52.63% lines covered

76 relevant lines. 40 lines covered and 36 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/module/attr_internal"
  3. 1 require "active_support/core_ext/module/attribute_accessors"
  4. 1 require "active_support/ordered_options"
  5. 1 require "action_view/log_subscriber"
  6. 1 require "action_view/helpers"
  7. 1 require "action_view/context"
  8. 1 require "action_view/template"
  9. 1 require "action_view/lookup_context"
  10. 1 module ActionView #:nodoc:
  11. # = Action View Base
  12. #
  13. # Action View templates can be written in several ways.
  14. # If the template file has a <tt>.erb</tt> extension, then it uses the erubi[https://rubygems.org/gems/erubi]
  15. # template system which can embed Ruby into an HTML document.
  16. # If the template file has a <tt>.builder</tt> extension, then Jim Weirich's Builder::XmlMarkup library is used.
  17. #
  18. # == ERB
  19. #
  20. # You trigger ERB by using embeddings such as <tt><% %></tt>, <tt><% -%></tt>, and <tt><%= %></tt>. The <tt><%= %></tt> tag set is used when you want output. Consider the
  21. # following loop for names:
  22. #
  23. # <b>Names of all the people</b>
  24. # <% @people.each do |person| %>
  25. # Name: <%= person.name %><br/>
  26. # <% end %>
  27. #
  28. # The loop is set up in regular embedding tags <tt><% %></tt>, and the name is written using the output embedding tag <tt><%= %></tt>. Note that this
  29. # is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
  30. #
  31. # <%# WRONG %>
  32. # Hi, Mr. <% puts "Frodo" %>
  33. #
  34. # If you absolutely must write from within a function use +concat+.
  35. #
  36. # When on a line that only contains whitespaces except for the tag, <tt><% %></tt> suppresses leading and trailing whitespace,
  37. # including the trailing newline. <tt><% %></tt> and <tt><%- -%></tt> are the same.
  38. # Note however that <tt><%= %></tt> and <tt><%= -%></tt> are different: only the latter removes trailing whitespaces.
  39. #
  40. # === Using sub templates
  41. #
  42. # Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
  43. # classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
  44. #
  45. # <%= render "shared/header" %>
  46. # Something really specific and terrific
  47. # <%= render "shared/footer" %>
  48. #
  49. # As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
  50. # result of the rendering. The output embedding writes it to the current template.
  51. #
  52. # But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
  53. # variables defined using the regular embedding tags. Like this:
  54. #
  55. # <% @page_title = "A Wonderful Hello" %>
  56. # <%= render "shared/header" %>
  57. #
  58. # Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
  59. #
  60. # <title><%= @page_title %></title>
  61. #
  62. # === Passing local variables to sub templates
  63. #
  64. # You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
  65. #
  66. # <%= render "shared/header", { headline: "Welcome", person: person } %>
  67. #
  68. # These can now be accessed in <tt>shared/header</tt> with:
  69. #
  70. # Headline: <%= headline %>
  71. # First name: <%= person.first_name %>
  72. #
  73. # The local variables passed to sub templates can be accessed as a hash using the <tt>local_assigns</tt> hash. This lets you access the
  74. # variables as:
  75. #
  76. # Headline: <%= local_assigns[:headline] %>
  77. #
  78. # This is useful in cases where you aren't sure if the local variable has been assigned. Alternatively, you could also use
  79. # <tt>defined? headline</tt> to first check if the variable has been assigned before using it.
  80. #
  81. # === Template caching
  82. #
  83. # By default, Rails will compile each template to a method in order to render it. When you alter a template,
  84. # Rails will check the file's modification time and recompile it in development mode.
  85. #
  86. # == Builder
  87. #
  88. # Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object
  89. # named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
  90. #
  91. # Here are some basic examples:
  92. #
  93. # xml.em("emphasized") # => <em>emphasized</em>
  94. # xml.em { xml.b("emph & bold") } # => <em><b>emph &amp; bold</b></em>
  95. # xml.a("A Link", "href" => "http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
  96. # xml.target("name" => "compile", "option" => "fast") # => <target option="fast" name="compile"\>
  97. # # NOTE: order of attributes is not specified.
  98. #
  99. # Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
  100. #
  101. # xml.div do
  102. # xml.h1(@person.name)
  103. # xml.p(@person.bio)
  104. # end
  105. #
  106. # would produce something like:
  107. #
  108. # <div>
  109. # <h1>David Heinemeier Hansson</h1>
  110. # <p>A product of Danish Design during the Winter of '79...</p>
  111. # </div>
  112. #
  113. # Here is a full-length RSS example actually used on Basecamp:
  114. #
  115. # xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
  116. # xml.channel do
  117. # xml.title(@feed_title)
  118. # xml.link(@url)
  119. # xml.description "Basecamp: Recent items"
  120. # xml.language "en-us"
  121. # xml.ttl "40"
  122. #
  123. # @recent_items.each do |item|
  124. # xml.item do
  125. # xml.title(item_title(item))
  126. # xml.description(item_description(item)) if item_description(item)
  127. # xml.pubDate(item_pubDate(item))
  128. # xml.guid(@person.firm.account.url + @recent_items.url(item))
  129. # xml.link(@person.firm.account.url + @recent_items.url(item))
  130. #
  131. # xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
  132. # end
  133. # end
  134. # end
  135. # end
  136. #
  137. # For more information on Builder please consult the {source
  138. # code}[https://github.com/jimweirich/builder].
  139. 1 class Base
  140. 1 include Helpers, ::ERB::Util, Context
  141. # Specify the proc used to decorate input tags that refer to attributes with errors.
  142. 1 cattr_accessor :field_error_proc, default: Proc.new { |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
  143. # How to complete the streaming when an exception occurs.
  144. # This is our best guess: first try to close the attribute, then the tag.
  145. 1 cattr_accessor :streaming_completion_on_exception, default: %("><script>window.location = "/500.html"</script></html>)
  146. # Specify whether rendering within namespaced controllers should prefix
  147. # the partial paths for ActiveModel objects with the namespace.
  148. # (e.g., an Admin::PostsController would render @post using /admin/posts/_post.erb)
  149. 1 class_attribute :prefix_partial_path_with_controller_namespace, default: true
  150. # Specify default_formats that can be rendered.
  151. 1 cattr_accessor :default_formats
  152. # Specify whether an error should be raised for missing translations
  153. 1 cattr_accessor :raise_on_missing_translations, default: false
  154. # Specify whether submit_tag should automatically disable on click
  155. 1 cattr_accessor :automatically_disable_submit_tag, default: true
  156. # Annotate rendered view with file names
  157. 1 cattr_accessor :annotate_rendered_view_with_filenames, default: false
  158. 1 class_attribute :_routes
  159. 1 class_attribute :logger
  160. 1 class << self
  161. 1 delegate :erb_trim_mode=, to: "ActionView::Template::Handlers::ERB"
  162. 1 def cache_template_loading
  163. ActionView::Resolver.caching?
  164. end
  165. 1 def cache_template_loading=(value)
  166. 1 ActionView::Resolver.caching = value
  167. end
  168. 1 def xss_safe? #:nodoc:
  169. true
  170. end
  171. 1 def with_empty_template_cache # :nodoc:
  172. subclass = Class.new(self) {
  173. # We can't implement these as self.class because subclasses will
  174. # share the same template cache as superclasses, so "changed?" won't work
  175. # correctly.
  176. define_method(:compiled_method_container) { subclass }
  177. define_singleton_method(:compiled_method_container) { subclass }
  178. def inspect
  179. "#<ActionView::Base:#{'%#016x' % (object_id << 1)}>"
  180. end
  181. }
  182. end
  183. 1 def changed?(other) # :nodoc:
  184. compiled_method_container != other.compiled_method_container
  185. end
  186. end
  187. 1 attr_reader :view_renderer, :lookup_context
  188. 1 attr_internal :config, :assigns
  189. 1 delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, to: :lookup_context
  190. 1 def assign(new_assigns) # :nodoc:
  191. @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
  192. end
  193. # :stopdoc:
  194. 1 def self.empty
  195. with_view_paths([])
  196. end
  197. 1 def self.with_view_paths(view_paths, assigns = {}, controller = nil)
  198. with_context ActionView::LookupContext.new(view_paths), assigns, controller
  199. end
  200. 1 def self.with_context(context, assigns = {}, controller = nil)
  201. new context, assigns, controller
  202. end
  203. # :startdoc:
  204. 1 def initialize(lookup_context, assigns, controller) #:nodoc:
  205. @_config = ActiveSupport::InheritableOptions.new
  206. @lookup_context = lookup_context
  207. @view_renderer = ActionView::Renderer.new @lookup_context
  208. @current_template = nil
  209. assign(assigns)
  210. assign_controller(controller)
  211. _prepare_context
  212. end
  213. 1 def _run(method, template, locals, buffer, add_to_stack: true, &block)
  214. _old_output_buffer, _old_virtual_path, _old_template = @output_buffer, @virtual_path, @current_template
  215. @current_template = template if add_to_stack
  216. @output_buffer = buffer
  217. public_send(method, locals, buffer, &block)
  218. ensure
  219. @output_buffer, @virtual_path, @current_template = _old_output_buffer, _old_virtual_path, _old_template
  220. end
  221. 1 def compiled_method_container
  222. raise NotImplementedError, <<~msg.squish
  223. Subclasses of ActionView::Base must implement `compiled_method_container`
  224. or use the class method `with_empty_template_cache` for constructing
  225. an ActionView::Base subclass that has an empty cache.
  226. msg
  227. end
  228. 1 def in_rendering_context(options)
  229. old_view_renderer = @view_renderer
  230. old_lookup_context = @lookup_context
  231. if !lookup_context.html_fallback_for_js && options[:formats]
  232. formats = Array(options[:formats])
  233. if formats == [:js]
  234. formats << :html
  235. end
  236. @lookup_context = lookup_context.with_prepended_formats(formats)
  237. @view_renderer = ActionView::Renderer.new @lookup_context
  238. end
  239. yield @view_renderer
  240. ensure
  241. @view_renderer = old_view_renderer
  242. @lookup_context = old_lookup_context
  243. end
  244. 1 ActiveSupport.run_load_hooks(:action_view, self)
  245. end
  246. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/context.rb

50.0% lines covered

10 relevant lines. 5 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. # = Action View Context
  4. #
  5. # Action View contexts are supplied to Action Controller to render a template.
  6. # The default Action View context is ActionView::Base.
  7. #
  8. # In order to work with Action Controller, a Context must just include this
  9. # module. The initialization of the variables used by the context
  10. # (@output_buffer, @view_flow, and @virtual_path) is responsibility of the
  11. # object that includes this module (although you can call _prepare_context
  12. # defined below).
  13. 1 module Context
  14. 1 attr_accessor :output_buffer, :view_flow
  15. # Prepares the context by setting the appropriate instance variables.
  16. 1 def _prepare_context
  17. @view_flow = OutputFlow.new
  18. @output_buffer = nil
  19. @virtual_path = nil
  20. end
  21. # Encapsulates the interaction with the view flow so it
  22. # returns the correct buffer on +yield+. This is usually
  23. # overwritten by helpers to add more behavior.
  24. 1 def _layout_for(name = nil)
  25. name ||= :layout
  26. view_flow.get(name).html_safe
  27. end
  28. end
  29. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags.rb

100.0% lines covered

37 relevant lines. 37 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. 1 module Helpers #:nodoc:
  4. 1 module Tags #:nodoc:
  5. 1 extend ActiveSupport::Autoload
  6. 1 eager_autoload do
  7. 1 autoload :Base
  8. 1 autoload :Translator
  9. 1 autoload :CheckBox
  10. 1 autoload :CollectionCheckBoxes
  11. 1 autoload :CollectionRadioButtons
  12. 1 autoload :CollectionSelect
  13. 1 autoload :ColorField
  14. 1 autoload :DateField
  15. 1 autoload :DateSelect
  16. 1 autoload :DatetimeField
  17. 1 autoload :DatetimeLocalField
  18. 1 autoload :DatetimeSelect
  19. 1 autoload :EmailField
  20. 1 autoload :FileField
  21. 1 autoload :GroupedCollectionSelect
  22. 1 autoload :HiddenField
  23. 1 autoload :Label
  24. 1 autoload :MonthField
  25. 1 autoload :NumberField
  26. 1 autoload :PasswordField
  27. 1 autoload :RadioButton
  28. 1 autoload :RangeField
  29. 1 autoload :SearchField
  30. 1 autoload :Select
  31. 1 autoload :TelField
  32. 1 autoload :TextArea
  33. 1 autoload :TextField
  34. 1 autoload :TimeField
  35. 1 autoload :TimeSelect
  36. 1 autoload :TimeZoneSelect
  37. 1 autoload :UrlField
  38. 1 autoload :WeekField
  39. end
  40. end
  41. end
  42. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags/base.rb

25.0% lines covered

108 relevant lines. 27 lines covered and 81 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. 1 module Helpers
  4. 1 module Tags # :nodoc:
  5. 1 class Base # :nodoc:
  6. 1 include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
  7. 1 include FormOptionsHelper
  8. 1 attr_reader :object
  9. 1 def initialize(object_name, method_name, template_object, options = {})
  10. @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
  11. @template_object = template_object
  12. @object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
  13. @object = retrieve_object(options.delete(:object))
  14. @skip_default_ids = options.delete(:skip_default_ids)
  15. @allow_method_names_outside_object = options.delete(:allow_method_names_outside_object)
  16. @options = options
  17. if Regexp.last_match
  18. @generate_indexed_names = true
  19. @auto_index = retrieve_autoindex(Regexp.last_match.pre_match)
  20. else
  21. @generate_indexed_names = false
  22. @auto_index = nil
  23. end
  24. end
  25. # This is what child classes implement.
  26. 1 def render
  27. raise NotImplementedError, "Subclasses must implement a render method"
  28. end
  29. 1 private
  30. 1 def value
  31. if @allow_method_names_outside_object
  32. object.public_send @method_name if object && object.respond_to?(@method_name)
  33. else
  34. object.public_send @method_name if object
  35. end
  36. end
  37. 1 def value_before_type_cast
  38. unless object.nil?
  39. method_before_type_cast = @method_name + "_before_type_cast"
  40. if value_came_from_user? && object.respond_to?(method_before_type_cast)
  41. object.public_send(method_before_type_cast)
  42. else
  43. value
  44. end
  45. end
  46. end
  47. 1 def value_came_from_user?
  48. method_name = "#{@method_name}_came_from_user?"
  49. !object.respond_to?(method_name) || object.public_send(method_name)
  50. end
  51. 1 def retrieve_object(object)
  52. if object
  53. object
  54. elsif @template_object.instance_variable_defined?("@#{@object_name}")
  55. @template_object.instance_variable_get("@#{@object_name}")
  56. end
  57. rescue NameError
  58. # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
  59. nil
  60. end
  61. 1 def retrieve_autoindex(pre_match)
  62. object = self.object || @template_object.instance_variable_get("@#{pre_match}")
  63. if object && object.respond_to?(:to_param)
  64. object.to_param
  65. else
  66. raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
  67. end
  68. end
  69. 1 def add_default_name_and_id_for_value(tag_value, options)
  70. if tag_value.nil?
  71. add_default_name_and_id(options)
  72. else
  73. specified_id = options["id"]
  74. add_default_name_and_id(options)
  75. if specified_id.blank? && options["id"].present?
  76. options["id"] += "_#{sanitized_value(tag_value)}"
  77. end
  78. end
  79. end
  80. 1 def add_default_name_and_id(options)
  81. index = name_and_id_index(options)
  82. options["name"] = options.fetch("name") { tag_name(options["multiple"], index) }
  83. if generate_ids?
  84. options["id"] = options.fetch("id") { tag_id(index) }
  85. if namespace = options.delete("namespace")
  86. options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
  87. end
  88. end
  89. end
  90. 1 def tag_name(multiple = false, index = nil)
  91. # a little duplication to construct fewer strings
  92. case
  93. when @object_name.empty?
  94. "#{sanitized_method_name}#{multiple ? "[]" : ""}"
  95. when index
  96. "#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
  97. else
  98. "#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
  99. end
  100. end
  101. 1 def tag_id(index = nil)
  102. # a little duplication to construct fewer strings
  103. case
  104. when @object_name.empty?
  105. sanitized_method_name.dup
  106. when index
  107. "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
  108. else
  109. "#{sanitized_object_name}_#{sanitized_method_name}"
  110. end
  111. end
  112. 1 def sanitized_object_name
  113. @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").delete_suffix("_")
  114. end
  115. 1 def sanitized_method_name
  116. @sanitized_method_name ||= @method_name.delete_suffix("?")
  117. end
  118. 1 def sanitized_value(value)
  119. value.to_s.gsub(/[\s\.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
  120. end
  121. 1 def select_content_tag(option_tags, options, html_options)
  122. html_options = html_options.stringify_keys
  123. add_default_name_and_id(html_options)
  124. if placeholder_required?(html_options)
  125. raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
  126. options[:include_blank] ||= true unless options[:prompt]
  127. end
  128. value = options.fetch(:selected) { value() }
  129. select = content_tag("select", add_options(option_tags, options, value), html_options)
  130. if html_options["multiple"] && options.fetch(:include_hidden, true)
  131. tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "") + select
  132. else
  133. select
  134. end
  135. end
  136. 1 def placeholder_required?(html_options)
  137. # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
  138. html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
  139. end
  140. 1 def add_options(option_tags, options, value = nil)
  141. if options[:include_blank]
  142. content = (options[:include_blank] if options[:include_blank].is_a?(String))
  143. label = (" " unless content)
  144. option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
  145. end
  146. if value.blank? && options[:prompt]
  147. tag_options = { value: "" }.tap do |prompt_opts|
  148. prompt_opts[:disabled] = true if options[:disabled] == ""
  149. prompt_opts[:selected] = true if options[:selected] == ""
  150. end
  151. option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
  152. end
  153. option_tags
  154. end
  155. 1 def name_and_id_index(options)
  156. if options.key?("index")
  157. options.delete("index") || ""
  158. elsif @generate_indexed_names
  159. @auto_index || ""
  160. end
  161. end
  162. 1 def generate_ids?
  163. !@skip_default_ids
  164. end
  165. end
  166. end
  167. end
  168. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/helpers/tags/placeholderable.rb

41.67% lines covered

12 relevant lines. 5 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. 1 module Helpers
  4. 1 module Tags # :nodoc:
  5. 1 module Placeholderable # :nodoc:
  6. 1 def initialize(*)
  7. super
  8. if tag_value = @options[:placeholder]
  9. placeholder = tag_value if tag_value.is_a?(String)
  10. method_and_value = tag_value.is_a?(TrueClass) ? @method_name : "#{@method_name}.#{tag_value}"
  11. placeholder ||= Tags::Translator
  12. .new(object, @object_name, method_and_value, scope: "helpers.placeholder")
  13. .translate
  14. placeholder ||= @method_name.humanize
  15. @options[:placeholder] = placeholder
  16. end
  17. end
  18. end
  19. end
  20. end
  21. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/layouts.rb

50.6% lines covered

83 relevant lines. 42 lines covered and 41 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_view/rendering"
  3. 1 require "active_support/core_ext/module/redefine_method"
  4. 1 module ActionView
  5. # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
  6. # repeated setups. The inclusion pattern has pages that look like this:
  7. #
  8. # <%= render "shared/header" %>
  9. # Hello World
  10. # <%= render "shared/footer" %>
  11. #
  12. # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
  13. # and if you ever want to change the structure of these two includes, you'll have to change all the templates.
  14. #
  15. # With layouts, you can flip it around and have the common structure know where to insert changing content. This means
  16. # that the header and footer are only mentioned in one place, like this:
  17. #
  18. # // The header part of this layout
  19. # <%= yield %>
  20. # // The footer part of this layout
  21. #
  22. # And then you have content pages that look like this:
  23. #
  24. # hello world
  25. #
  26. # At rendering time, the content page is computed and then inserted in the layout, like this:
  27. #
  28. # // The header part of this layout
  29. # hello world
  30. # // The footer part of this layout
  31. #
  32. # == Accessing shared variables
  33. #
  34. # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
  35. # references that won't materialize before rendering time:
  36. #
  37. # <h1><%= @page_title %></h1>
  38. # <%= yield %>
  39. #
  40. # ...and content pages that fulfill these references _at_ rendering time:
  41. #
  42. # <% @page_title = "Welcome" %>
  43. # Off-world colonies offers you a chance to start a new life
  44. #
  45. # The result after rendering is:
  46. #
  47. # <h1>Welcome</h1>
  48. # Off-world colonies offers you a chance to start a new life
  49. #
  50. # == Layout assignment
  51. #
  52. # You can either specify a layout declaratively (using the #layout class method) or give
  53. # it the same name as your controller, and place it in <tt>app/views/layouts</tt>.
  54. # If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance.
  55. #
  56. # For instance, if you have PostsController and a template named <tt>app/views/layouts/posts.html.erb</tt>,
  57. # that template will be used for all actions in PostsController and controllers inheriting
  58. # from PostsController.
  59. #
  60. # If you use a module, for instance Weblog::PostsController, you will need a template named
  61. # <tt>app/views/layouts/weblog/posts.html.erb</tt>.
  62. #
  63. # Since all your controllers inherit from ApplicationController, they will use
  64. # <tt>app/views/layouts/application.html.erb</tt> if no other layout is specified
  65. # or provided.
  66. #
  67. # == Inheritance Examples
  68. #
  69. # class BankController < ActionController::Base
  70. # # bank.html.erb exists
  71. #
  72. # class ExchangeController < BankController
  73. # # exchange.html.erb exists
  74. #
  75. # class CurrencyController < BankController
  76. #
  77. # class InformationController < BankController
  78. # layout "information"
  79. #
  80. # class TellerController < InformationController
  81. # # teller.html.erb exists
  82. #
  83. # class EmployeeController < InformationController
  84. # # employee.html.erb exists
  85. # layout nil
  86. #
  87. # class VaultController < BankController
  88. # layout :access_level_layout
  89. #
  90. # class TillController < BankController
  91. # layout false
  92. #
  93. # In these examples, we have three implicit lookup scenarios:
  94. # * The +BankController+ uses the "bank" layout.
  95. # * The +ExchangeController+ uses the "exchange" layout.
  96. # * The +CurrencyController+ inherits the layout from BankController.
  97. #
  98. # However, when a layout is explicitly set, the explicitly set layout wins:
  99. # * The +InformationController+ uses the "information" layout, explicitly set.
  100. # * The +TellerController+ also uses the "information" layout, because the parent explicitly set it.
  101. # * The +EmployeeController+ uses the "employee" layout, because it set the layout to +nil+, resetting the parent configuration.
  102. # * The +VaultController+ chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
  103. # * The +TillController+ does not use a layout at all.
  104. #
  105. # == Types of layouts
  106. #
  107. # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
  108. # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
  109. # be done either by specifying a method reference as a symbol or using an inline method (as a proc).
  110. #
  111. # The method reference is the preferred approach to variable layouts and is used like this:
  112. #
  113. # class WeblogController < ActionController::Base
  114. # layout :writers_and_readers
  115. #
  116. # def index
  117. # # fetching posts
  118. # end
  119. #
  120. # private
  121. # def writers_and_readers
  122. # logged_in? ? "writer_layout" : "reader_layout"
  123. # end
  124. # end
  125. #
  126. # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
  127. # is logged in or not.
  128. #
  129. # If you want to use an inline method, such as a proc, do something like this:
  130. #
  131. # class WeblogController < ActionController::Base
  132. # layout proc { |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
  133. # end
  134. #
  135. # If an argument isn't given to the proc, it's evaluated in the context of
  136. # the current controller anyway.
  137. #
  138. # class WeblogController < ActionController::Base
  139. # layout proc { logged_in? ? "writer_layout" : "reader_layout" }
  140. # end
  141. #
  142. # Of course, the most common way of specifying a layout is still just as a plain template name:
  143. #
  144. # class WeblogController < ActionController::Base
  145. # layout "weblog_standard"
  146. # end
  147. #
  148. # The template will be looked always in <tt>app/views/layouts/</tt> folder. But you can point
  149. # <tt>layouts</tt> folder direct also. <tt>layout "layouts/demo"</tt> is the same as <tt>layout "demo"</tt>.
  150. #
  151. # Setting the layout to +nil+ forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
  152. # Setting it to +nil+ is useful to re-enable template lookup overriding a previous configuration set in the parent:
  153. #
  154. # class ApplicationController < ActionController::Base
  155. # layout "application"
  156. # end
  157. #
  158. # class PostsController < ApplicationController
  159. # # Will use "application" layout
  160. # end
  161. #
  162. # class CommentsController < ApplicationController
  163. # # Will search for "comments" layout and fallback "application" layout
  164. # layout nil
  165. # end
  166. #
  167. # == Conditional layouts
  168. #
  169. # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
  170. # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
  171. # <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
  172. #
  173. # class WeblogController < ActionController::Base
  174. # layout "weblog_standard", except: :rss
  175. #
  176. # # ...
  177. #
  178. # end
  179. #
  180. # This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
  181. # be rendered directly, without wrapping a layout around the rendered view.
  182. #
  183. # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
  184. # #<tt>except: [ :rss, :text_only ]</tt> is valid, as is <tt>except: :rss</tt>.
  185. #
  186. # == Using a different layout in the action render call
  187. #
  188. # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
  189. # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
  190. # You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
  191. #
  192. # class WeblogController < ActionController::Base
  193. # layout "weblog_standard"
  194. #
  195. # def help
  196. # render action: "help", layout: "help"
  197. # end
  198. # end
  199. #
  200. # This will override the controller-wide "weblog_standard" layout, and will render the help action with the "help" layout instead.
  201. 1 module Layouts
  202. 1 extend ActiveSupport::Concern
  203. 1 include ActionView::Rendering
  204. 1 included do
  205. 2 class_attribute :_layout, instance_accessor: false
  206. 2 class_attribute :_layout_conditions, instance_accessor: false, default: {}
  207. 2 _write_layout_method
  208. end
  209. 1 delegate :_layout_conditions, to: :class
  210. 1 module ClassMethods
  211. 1 def inherited(klass) # :nodoc:
  212. 1 super
  213. 1 klass._write_layout_method
  214. end
  215. # This module is mixed in if layout conditions are provided. This means
  216. # that if no layout conditions are used, this method is not used
  217. 1 module LayoutConditions # :nodoc:
  218. 1 private
  219. # Determines whether the current action has a layout definition by
  220. # checking the action name against the :only and :except conditions
  221. # set by the <tt>layout</tt> method.
  222. #
  223. # ==== Returns
  224. # * <tt>Boolean</tt> - True if the action has a layout definition, false otherwise.
  225. 1 def _conditional_layout?
  226. return unless super
  227. conditions = _layout_conditions
  228. if only = conditions[:only]
  229. only.include?(action_name)
  230. elsif except = conditions[:except]
  231. !except.include?(action_name)
  232. else
  233. true
  234. end
  235. end
  236. end
  237. # Specify the layout to use for this class.
  238. #
  239. # If the specified layout is a:
  240. # String:: the String is the template name
  241. # Symbol:: call the method specified by the symbol
  242. # Proc:: call the passed Proc
  243. # false:: There is no layout
  244. # true:: raise an ArgumentError
  245. # nil:: Force default layout behavior with inheritance
  246. #
  247. # Return value of +Proc+ and +Symbol+ arguments should be +String+, +false+, +true+ or +nil+
  248. # with the same meaning as described above.
  249. # ==== Parameters
  250. # * <tt>layout</tt> - The layout to use.
  251. #
  252. # ==== Options (conditions)
  253. # * :only - A list of actions to apply this layout to.
  254. # * :except - Apply this layout to all actions but this one.
  255. 1 def layout(layout, conditions = {})
  256. include LayoutConditions unless conditions.empty?
  257. conditions.each { |k, v| conditions[k] = Array(v).map(&:to_s) }
  258. self._layout_conditions = conditions
  259. self._layout = layout
  260. _write_layout_method
  261. end
  262. # Creates a _layout method to be called by _default_layout .
  263. #
  264. # If a layout is not explicitly mentioned then look for a layout with the controller's name.
  265. # if nothing is found then try same procedure to find super class's layout.
  266. 1 def _write_layout_method # :nodoc:
  267. 3 silence_redefinition_of_method(:_layout)
  268. 3 prefixes = /\blayouts/.match?(_implied_layout_name) ? [] : ["layouts"]
  269. 3 default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
  270. 3 name_clause = if name
  271. 3 default_behavior
  272. else
  273. <<-RUBY
  274. super
  275. RUBY
  276. end
  277. layout_definition = \
  278. 3 case _layout
  279. when String
  280. _layout.inspect
  281. when Symbol
  282. <<-RUBY
  283. #{_layout}.tap do |layout|
  284. return #{default_behavior} if layout.nil?
  285. unless layout.is_a?(String) || !layout
  286. raise ArgumentError, "Your layout method :#{_layout} returned \#{layout}. It " \
  287. "should have returned a String, false, or nil"
  288. end
  289. end
  290. RUBY
  291. when Proc
  292. define_method :_layout_from_proc, &_layout
  293. private :_layout_from_proc
  294. <<-RUBY
  295. result = _layout_from_proc(#{_layout.arity == 0 ? '' : 'self'})
  296. return #{default_behavior} if result.nil?
  297. result
  298. RUBY
  299. when false
  300. nil
  301. when true
  302. raise ArgumentError, "Layouts must be specified as a String, Symbol, Proc, false, or nil"
  303. when nil
  304. 3 name_clause
  305. end
  306. 3 class_eval <<-RUBY, __FILE__, __LINE__ + 1
  307. # frozen_string_literal: true
  308. def _layout(lookup_context, formats)
  309. if _conditional_layout?
  310. #{layout_definition}
  311. else
  312. #{name_clause}
  313. end
  314. end
  315. private :_layout
  316. RUBY
  317. end
  318. 1 private
  319. # If no layout is supplied, look for a template named the return
  320. # value of this method.
  321. #
  322. # ==== Returns
  323. # * <tt>String</tt> - A template name
  324. 1 def _implied_layout_name
  325. 6 controller_path
  326. end
  327. end
  328. 1 def _normalize_options(options) # :nodoc:
  329. super
  330. if _include_layout?(options)
  331. layout = options.delete(:layout) { :default }
  332. options[:layout] = _layout_for_option(layout)
  333. end
  334. end
  335. 1 attr_internal_writer :action_has_layout
  336. 1 def initialize(*) # :nodoc:
  337. @_action_has_layout = true
  338. super
  339. end
  340. # Controls whether an action should be rendered using a layout.
  341. # If you want to disable any <tt>layout</tt> settings for the
  342. # current action so that it is rendered without a layout then
  343. # either override this method in your controller to return false
  344. # for that action or set the <tt>action_has_layout</tt> attribute
  345. # to false before rendering.
  346. 1 def action_has_layout?
  347. @_action_has_layout
  348. end
  349. 1 private
  350. 1 def _conditional_layout?
  351. true
  352. end
  353. # This will be overwritten by _write_layout_method
  354. 1 def _layout(*); end
  355. # Determine the layout for a given name, taking into account the name type.
  356. #
  357. # ==== Parameters
  358. # * <tt>name</tt> - The name of the template
  359. 1 def _layout_for_option(name)
  360. case name
  361. when String then _normalize_layout(name)
  362. when Proc then name
  363. when true then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, true) }
  364. when :default then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, false) }
  365. when false, nil then nil
  366. else
  367. raise ArgumentError,
  368. "String, Proc, :default, true, or false, expected for `layout'; you passed #{name.inspect}"
  369. end
  370. end
  371. 1 def _normalize_layout(value)
  372. value.is_a?(String) && !value.match?(/\blayouts/) ? "layouts/#{value}" : value
  373. end
  374. # Returns the default layout for this controller.
  375. # Optionally raises an exception if the layout could not be found.
  376. #
  377. # ==== Parameters
  378. # * <tt>formats</tt> - The formats accepted to this layout
  379. # * <tt>require_layout</tt> - If set to +true+ and layout is not found,
  380. # an +ArgumentError+ exception is raised (defaults to +false+)
  381. #
  382. # ==== Returns
  383. # * <tt>template</tt> - The template object for the default layout (or +nil+)
  384. 1 def _default_layout(lookup_context, formats, require_layout = false)
  385. begin
  386. value = _layout(lookup_context, formats) if action_has_layout?
  387. rescue NameError => e
  388. raise e, "Could not render layout: #{e.message}"
  389. end
  390. if require_layout && action_has_layout? && !value
  391. raise ArgumentError,
  392. "There was no default layout for #{self.class} in #{view_paths.inspect}"
  393. end
  394. _normalize_layout(value)
  395. end
  396. 1 def _include_layout?(options)
  397. (options.keys & [:body, :plain, :html, :inline, :partial]).empty? || options.key?(:layout)
  398. end
  399. end
  400. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/log_subscriber.rb

33.87% lines covered

62 relevant lines. 21 lines covered and 41 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/log_subscriber"
  3. 1 module ActionView
  4. # = Action View Log Subscriber
  5. #
  6. # Provides functionality so that Rails can output logs from Action View.
  7. 1 class LogSubscriber < ActiveSupport::LogSubscriber
  8. 1 VIEWS_PATTERN = /^app\/views\//
  9. 1 def initialize
  10. 1 @root = nil
  11. 1 super
  12. end
  13. 1 def render_template(event)
  14. info do
  15. message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
  16. message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
  17. message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
  18. end
  19. end
  20. 1 def render_partial(event)
  21. debug do
  22. message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
  23. message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
  24. message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
  25. message << " #{cache_message(event.payload)}" unless event.payload[:cache_hit].nil?
  26. message
  27. end
  28. end
  29. 1 def render_layout(event)
  30. info do
  31. message = +" Rendered layout #{from_rails_root(event.payload[:identifier])}"
  32. message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
  33. end
  34. end
  35. 1 def render_collection(event)
  36. identifier = event.payload[:identifier] || "templates"
  37. debug do
  38. message = +" Rendered collection of #{from_rails_root(identifier)}"
  39. message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
  40. message << " #{render_count(event.payload)} (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
  41. message
  42. end
  43. end
  44. 1 def start(name, id, payload)
  45. log_rendering_start(payload, name)
  46. super
  47. end
  48. 1 def logger
  49. ActionView::Base.logger
  50. end
  51. 1 private
  52. 1 EMPTY = ""
  53. 1 def from_rails_root(string) # :doc:
  54. string = string.sub(rails_root, EMPTY)
  55. string.sub!(VIEWS_PATTERN, EMPTY)
  56. string
  57. end
  58. 1 def rails_root # :doc:
  59. @root ||= "#{Rails.root}/"
  60. end
  61. 1 def render_count(payload) # :doc:
  62. if payload[:cache_hits]
  63. "[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
  64. else
  65. "[#{payload[:count]} times]"
  66. end
  67. end
  68. 1 def cache_message(payload) # :doc:
  69. case payload[:cache_hit]
  70. when :hit
  71. "[cache hit]"
  72. when :miss
  73. "[cache miss]"
  74. end
  75. end
  76. 1 def log_rendering_start(payload, name)
  77. debug do
  78. qualifier =
  79. if name == "render_template.action_view"
  80. ""
  81. elsif name == "render_layout.action_view"
  82. "layout "
  83. end
  84. return unless qualifier
  85. message = +" Rendering #{qualifier}#{from_rails_root(payload[:identifier])}"
  86. message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
  87. message
  88. end
  89. end
  90. end
  91. end
  92. 1 ActionView::LogSubscriber.attach_to :action_view

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/lookup_context.rb

61.15% lines covered

157 relevant lines. 96 lines covered and 61 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "concurrent/map"
  3. 1 require "active_support/core_ext/module/attribute_accessors"
  4. 1 require "action_view/template/resolver"
  5. 1 module ActionView
  6. # = Action View Lookup Context
  7. #
  8. # <tt>LookupContext</tt> is the object responsible for holding all information
  9. # required for looking up templates, i.e. view paths and details.
  10. # <tt>LookupContext</tt> is also responsible for generating a key, given to
  11. # view paths, used in the resolver cache lookup. Since this key is generated
  12. # only once during the request, it speeds up all cache accesses.
  13. 1 class LookupContext #:nodoc:
  14. 1 attr_accessor :prefixes, :rendered_format
  15. 1 mattr_accessor :fallbacks, default: FallbackFileSystemResolver.instances
  16. 1 mattr_accessor :registered_details, default: []
  17. 1 def self.register_detail(name, &block)
  18. 4 registered_details << name
  19. 4 Accessors::DEFAULT_PROCS[name] = block
  20. 4 Accessors.define_method(:"default_#{name}", &block)
  21. 4 Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
  22. def #{name}
  23. @details[:#{name}] || []
  24. end
  25. def #{name}=(value)
  26. value = value.present? ? Array(value) : default_#{name}
  27. _set_detail(:#{name}, value) if value != @details[:#{name}]
  28. end
  29. METHOD
  30. end
  31. # Holds accessors for the registered details.
  32. 1 module Accessors #:nodoc:
  33. 1 DEFAULT_PROCS = {}
  34. end
  35. 1 register_detail(:locale) do
  36. 1 locales = [I18n.locale]
  37. 1 locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks
  38. 1 locales << I18n.default_locale
  39. 1 locales.uniq!
  40. 1 locales
  41. end
  42. 2 register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
  43. 2 register_detail(:variants) { [] }
  44. 2 register_detail(:handlers) { Template::Handlers.extensions }
  45. 1 class DetailsKey #:nodoc:
  46. 1 alias :eql? :equal?
  47. 1 @details_keys = Concurrent::Map.new
  48. 1 @digest_cache = Concurrent::Map.new
  49. 1 @view_context_mutex = Mutex.new
  50. 1 def self.digest_cache(details)
  51. @digest_cache[details_cache_key(details)] ||= Concurrent::Map.new
  52. end
  53. 1 def self.details_cache_key(details)
  54. if details[:formats]
  55. details = details.dup
  56. details[:formats] &= Template::Types.symbols
  57. end
  58. @details_keys[details] ||= Object.new
  59. end
  60. 1 def self.clear
  61. 2 ActionView::ViewPaths.all_view_paths.each do |path_set|
  62. 6 path_set.each(&:clear_cache)
  63. end
  64. 2 ActionView::LookupContext.fallbacks.each(&:clear_cache)
  65. 2 @view_context_class = nil
  66. 2 @details_keys.clear
  67. 2 @digest_cache.clear
  68. end
  69. 1 def self.digest_caches
  70. @digest_cache.values
  71. end
  72. 1 def self.view_context_class(klass)
  73. @view_context_mutex.synchronize do
  74. @view_context_class ||= klass.with_empty_template_cache
  75. end
  76. end
  77. end
  78. # Add caching behavior on top of Details.
  79. 1 module DetailsCache
  80. 1 attr_accessor :cache
  81. # Calculate the details key. Remove the handlers from calculation to improve performance
  82. # since the user cannot modify it explicitly.
  83. 1 def details_key #:nodoc:
  84. @details_key ||= DetailsKey.details_cache_key(@details) if @cache
  85. end
  86. # Temporary skip passing the details_key forward.
  87. 1 def disable_cache
  88. old_value, @cache = @cache, false
  89. yield
  90. ensure
  91. @cache = old_value
  92. end
  93. 1 private
  94. 1 def _set_detail(key, value) # :doc:
  95. 1 @details = @details.dup if @digest_cache || @details_key
  96. 1 @digest_cache = nil
  97. 1 @details_key = nil
  98. 1 @details[key] = value
  99. end
  100. end
  101. # Helpers related to template lookup using the lookup context information.
  102. 1 module ViewPaths
  103. 1 attr_reader :view_paths, :html_fallback_for_js
  104. 1 def find(name, prefixes = [], partial = false, keys = [], options = {})
  105. @view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
  106. end
  107. 1 alias :find_template :find
  108. 1 def find_all(name, prefixes = [], partial = false, keys = [], options = {})
  109. @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
  110. end
  111. 1 def exists?(name, prefixes = [], partial = false, keys = [], **options)
  112. @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
  113. end
  114. 1 alias :template_exists? :exists?
  115. 1 def any?(name, prefixes = [], partial = false)
  116. @view_paths.exists?(*args_for_any(name, prefixes, partial))
  117. end
  118. 1 alias :any_templates? :any?
  119. # Adds fallbacks to the view paths. Useful in cases when you are rendering
  120. # a :file.
  121. 1 def with_fallbacks
  122. view_paths = build_view_paths((@view_paths.paths + self.class.fallbacks).uniq)
  123. if block_given?
  124. raise ArgumentError, <<~eowarn.squish
  125. Calling `with_fallbacks` with a block is not supported. Call methods on
  126. the lookup context returned by `with_fallbacks` instead.
  127. eowarn
  128. else
  129. ActionView::LookupContext.new(view_paths, @details, @prefixes)
  130. end
  131. end
  132. 1 private
  133. # Whenever setting view paths, makes a copy so that we can manipulate them in
  134. # instance objects as we wish.
  135. 1 def build_view_paths(paths)
  136. 1 ActionView::PathSet.new(Array(paths))
  137. end
  138. 1 def args_for_lookup(name, prefixes, partial, keys, details_options)
  139. name, prefixes = normalize_name(name, prefixes)
  140. details, details_key = detail_args_for(details_options)
  141. [name, prefixes, partial || false, details, details_key, keys]
  142. end
  143. # Compute details hash and key according to user options (e.g. passed from #render).
  144. 1 def detail_args_for(options) # :doc:
  145. return @details, details_key if options.empty? # most common path.
  146. user_details = @details.merge(options)
  147. if @cache
  148. details_key = DetailsKey.details_cache_key(user_details)
  149. else
  150. details_key = nil
  151. end
  152. [user_details, details_key]
  153. end
  154. 1 def args_for_any(name, prefixes, partial)
  155. name, prefixes = normalize_name(name, prefixes)
  156. details, details_key = detail_args_for_any
  157. [name, prefixes, partial || false, details, details_key]
  158. end
  159. 1 def detail_args_for_any
  160. @detail_args_for_any ||= begin
  161. details = {}
  162. registered_details.each do |k|
  163. if k == :variants
  164. details[k] = :any
  165. else
  166. details[k] = Accessors::DEFAULT_PROCS[k].call
  167. end
  168. end
  169. if @cache
  170. [details, DetailsKey.details_cache_key(details)]
  171. else
  172. [details, nil]
  173. end
  174. end
  175. end
  176. # Support legacy foo.erb names even though we now ignore .erb
  177. # as well as incorrectly putting part of the path in the template
  178. # name instead of the prefix.
  179. 1 def normalize_name(name, prefixes)
  180. prefixes = prefixes.presence
  181. parts = name.to_s.split("/")
  182. parts.shift if parts.first.empty?
  183. name = parts.pop
  184. return name, prefixes || [""] if parts.empty?
  185. parts = parts.join("/")
  186. prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
  187. return name, prefixes
  188. end
  189. end
  190. 1 include Accessors
  191. 1 include DetailsCache
  192. 1 include ViewPaths
  193. 1 def initialize(view_paths, details = {}, prefixes = [])
  194. 1 @details_key = nil
  195. 1 @digest_cache = nil
  196. 1 @cache = true
  197. 1 @prefixes = prefixes
  198. 1 @details = initialize_details({}, details)
  199. 1 @view_paths = build_view_paths(view_paths)
  200. end
  201. 1 def digest_cache
  202. @digest_cache ||= DetailsKey.digest_cache(@details)
  203. end
  204. 1 def with_prepended_formats(formats)
  205. details = @details.dup
  206. details[:formats] = formats
  207. self.class.new(@view_paths, details, @prefixes)
  208. end
  209. 1 def initialize_details(target, details)
  210. 1 registered_details.each do |k|
  211. 4 target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
  212. end
  213. 1 target
  214. end
  215. 1 private :initialize_details
  216. # Override formats= to expand ["*/*"] values and automatically
  217. # add :html as fallback to :js.
  218. 1 def formats=(values)
  219. 1 if values
  220. 1 values = values.dup
  221. 1 values.concat(default_formats) if values.delete "*/*"
  222. 1 values.uniq!
  223. 1 invalid_values = (values - Template::Types.symbols)
  224. 1 unless invalid_values.empty?
  225. raise ArgumentError, "Invalid formats: #{invalid_values.map(&:inspect).join(", ")}"
  226. end
  227. 1 if values == [:js]
  228. values << :html
  229. @html_fallback_for_js = true
  230. end
  231. end
  232. 1 super(values)
  233. end
  234. # Override locale to return a symbol instead of array.
  235. 1 def locale
  236. @details[:locale].first
  237. end
  238. # Overload locale= to also set the I18n.locale. If the current I18n.config object responds
  239. # to original_config, it means that it has a copy of the original I18n configuration and it's
  240. # acting as proxy, which we need to skip.
  241. 1 def locale=(value)
  242. if value
  243. config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config
  244. config.locale = value
  245. end
  246. super(default_locale)
  247. end
  248. end
  249. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/path_set.rb

59.52% lines covered

42 relevant lines. 25 lines covered and 17 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView #:nodoc:
  3. # = Action View PathSet
  4. #
  5. # This class is used to store and access paths in Action View. A number of
  6. # operations are defined so that you can search among the paths in this
  7. # set and also perform operations on other +PathSet+ objects.
  8. #
  9. # A +LookupContext+ will use a +PathSet+ to store the paths in its context.
  10. 1 class PathSet #:nodoc:
  11. 1 include Enumerable
  12. 1 attr_reader :paths
  13. 1 delegate :[], :include?, :pop, :size, :each, to: :paths
  14. 1 def initialize(paths = [])
  15. 13 @paths = typecast paths
  16. end
  17. 1 def initialize_copy(other)
  18. @paths = other.paths.dup
  19. self
  20. end
  21. 1 def to_ary
  22. 10 paths.dup
  23. end
  24. 1 def compact
  25. PathSet.new paths.compact
  26. end
  27. 1 def +(array)
  28. PathSet.new(paths + array)
  29. end
  30. 1 %w(<< concat push insert unshift).each do |method|
  31. 5 class_eval <<-METHOD, __FILE__, __LINE__ + 1
  32. def #{method}(*args)
  33. paths.#{method}(*typecast(args))
  34. end
  35. METHOD
  36. end
  37. 1 def find(*args)
  38. find_all(*args).first || raise(MissingTemplate.new(self, *args))
  39. end
  40. 1 def find_all(path, prefixes = [], *args)
  41. _find_all path, prefixes, args
  42. end
  43. 1 def exists?(path, prefixes, *args)
  44. find_all(path, prefixes, *args).any?
  45. end
  46. 1 def find_all_with_query(query) # :nodoc:
  47. paths.each do |resolver|
  48. templates = resolver.find_all_with_query(query)
  49. return templates unless templates.empty?
  50. end
  51. []
  52. end
  53. 1 private
  54. 1 def _find_all(path, prefixes, args)
  55. prefixes = [prefixes] if String === prefixes
  56. prefixes.each do |prefix|
  57. paths.each do |resolver|
  58. templates = resolver.find_all(path, prefix, *args)
  59. return templates unless templates.empty?
  60. end
  61. end
  62. []
  63. end
  64. 1 def typecast(paths)
  65. 13 paths.map do |path|
  66. 21 case path
  67. when Pathname, String
  68. 9 OptimizedFileSystemResolver.new path.to_s
  69. else
  70. 12 path
  71. end
  72. end
  73. end
  74. end
  75. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/rendering.rb

32.53% lines covered

83 relevant lines. 27 lines covered and 56 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_view/view_paths"
  3. 1 module ActionView
  4. # This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
  5. # it will trigger the lookup_context and consequently expire the cache.
  6. 1 class I18nProxy < ::I18n::Config #:nodoc:
  7. 1 attr_reader :original_config, :lookup_context
  8. 1 def initialize(original_config, lookup_context)
  9. original_config = original_config.original_config if original_config.respond_to?(:original_config)
  10. @original_config, @lookup_context = original_config, lookup_context
  11. end
  12. 1 def locale
  13. @original_config.locale
  14. end
  15. 1 def locale=(value)
  16. @lookup_context.locale = value
  17. end
  18. end
  19. 1 module Rendering
  20. 1 extend ActiveSupport::Concern
  21. 1 include ActionView::ViewPaths
  22. 1 attr_reader :rendered_format
  23. 1 def initialize
  24. @rendered_format = nil
  25. super
  26. end
  27. # Overwrite process to set up I18n proxy.
  28. 1 def process(*) #:nodoc:
  29. old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
  30. super
  31. ensure
  32. I18n.config = old_config
  33. end
  34. 1 module ClassMethods
  35. 1 def _routes
  36. end
  37. 1 def _helpers
  38. end
  39. 1 def build_view_context_class(klass, supports_path, routes, helpers)
  40. Class.new(klass) do
  41. if routes
  42. include routes.url_helpers(supports_path)
  43. include routes.mounted_helpers
  44. end
  45. if helpers
  46. include helpers
  47. end
  48. end
  49. end
  50. 1 def view_context_class
  51. klass = ActionView::LookupContext::DetailsKey.view_context_class(ActionView::Base)
  52. @view_context_class ||= build_view_context_class(klass, supports_path?, _routes, _helpers)
  53. if klass.changed?(@view_context_class)
  54. @view_context_class = build_view_context_class(klass, supports_path?, _routes, _helpers)
  55. end
  56. @view_context_class
  57. end
  58. end
  59. 1 def view_context_class
  60. self.class.view_context_class
  61. end
  62. # An instance of a view class. The default view class is ActionView::Base.
  63. #
  64. # The view class must have the following methods:
  65. #
  66. # * <tt>View.new(lookup_context, assigns, controller)</tt> — Create a new
  67. # ActionView instance for a controller and we can also pass the arguments.
  68. #
  69. # * <tt>View#render(option)</tt> — Returns String with the rendered template.
  70. #
  71. # Override this method in a module to change the default behavior.
  72. 1 def view_context
  73. view_context_class.new(lookup_context, view_assigns, self)
  74. end
  75. # Returns an object that is able to render templates.
  76. 1 def view_renderer # :nodoc:
  77. # Lifespan: Per controller
  78. @_view_renderer ||= ActionView::Renderer.new(lookup_context)
  79. end
  80. 1 def render_to_body(options = {})
  81. _process_options(options)
  82. _render_template(options)
  83. end
  84. 1 private
  85. # Find and render a template based on the options given.
  86. 1 def _render_template(options)
  87. variant = options.delete(:variant)
  88. assigns = options.delete(:assigns)
  89. context = view_context
  90. context.assign assigns if assigns
  91. lookup_context.variants = variant if variant
  92. rendered_template = context.in_rendering_context(options) do |renderer|
  93. renderer.render_to_object(context, options)
  94. end
  95. rendered_format = rendered_template.format || lookup_context.formats.first
  96. @rendered_format = Template::Types[rendered_format]
  97. rendered_template.body
  98. end
  99. # Assign the rendered format to look up context.
  100. 1 def _process_format(format)
  101. super
  102. lookup_context.formats = [format.to_sym] if format.to_sym
  103. end
  104. # Normalize args by converting render "foo" to render :action => "foo" and
  105. # render "foo/bar" to render :template => "foo/bar".
  106. 1 def _normalize_args(action = nil, options = {})
  107. options = super(action, options)
  108. case action
  109. when NilClass
  110. when Hash
  111. options = action
  112. when String, Symbol
  113. action = action.to_s
  114. key = action.include?(?/) ? :template : :action
  115. options[key] = action
  116. else
  117. if action.respond_to?(:permitted?) && action.permitted?
  118. options = action
  119. elsif action.respond_to?(:render_in)
  120. options[:renderable] = action
  121. else
  122. options[:partial] = action
  123. end
  124. end
  125. options
  126. end
  127. # Normalize options.
  128. 1 def _normalize_options(options)
  129. options = super(options)
  130. if options[:partial] == true
  131. options[:partial] = action_name
  132. end
  133. if (options.keys & [:partial, :file, :template]).empty?
  134. options[:prefixes] ||= _prefixes
  135. end
  136. options[:template] ||= (options[:action] || action_name).to_s
  137. options
  138. end
  139. end
  140. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/routing_url_for.rb

25.64% lines covered

39 relevant lines. 10 lines covered and 29 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_dispatch/routing/polymorphic_routes"
  3. 1 module ActionView
  4. 1 module RoutingUrlFor
  5. # Returns the URL for the set of +options+ provided. This takes the
  6. # same options as +url_for+ in Action Controller (see the
  7. # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
  8. # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
  9. # instead of the fully qualified URL like "http://example.com/controller/action".
  10. #
  11. # ==== Options
  12. # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
  13. # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
  14. # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
  15. # is currently not recommended since it breaks caching.
  16. # * <tt>:host</tt> - Overrides the default (current) host if provided.
  17. # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
  18. # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
  19. # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
  20. #
  21. # ==== Relying on named routes
  22. #
  23. # Passing a record (like an Active Record) instead of a hash as the options parameter will
  24. # trigger the named route for that record. The lookup will happen on the name of the class. So passing a
  25. # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
  26. # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
  27. #
  28. # ==== Implicit Controller Namespacing
  29. #
  30. # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
  31. #
  32. # ==== Examples
  33. # <%= url_for(action: 'index') %>
  34. # # => /blogs/
  35. #
  36. # <%= url_for(action: 'find', controller: 'books') %>
  37. # # => /books/find
  38. #
  39. # <%= url_for(action: 'login', controller: 'members', only_path: false, protocol: 'https') %>
  40. # # => https://www.example.com/members/login/
  41. #
  42. # <%= url_for(action: 'play', anchor: 'player') %>
  43. # # => /messages/play/#player
  44. #
  45. # <%= url_for(action: 'jump', anchor: 'tax&ship') %>
  46. # # => /testing/jump/#tax&ship
  47. #
  48. # <%= url_for(Workshop.new) %>
  49. # # relies on Workshop answering a persisted? call (and in this case returning false)
  50. # # => /workshops
  51. #
  52. # <%= url_for(@workshop) %>
  53. # # calls @workshop.to_param which by default returns the id
  54. # # => /workshops/5
  55. #
  56. # # to_param can be re-defined in a model to provide different URL names:
  57. # # => /workshops/1-workshop-name
  58. #
  59. # <%= url_for("http://www.example.com") %>
  60. # # => http://www.example.com
  61. #
  62. # <%= url_for(:back) %>
  63. # # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
  64. # # => http://www.example.com
  65. #
  66. # <%= url_for(:back) %>
  67. # # if request.env["HTTP_REFERER"] is not set or is blank
  68. # # => javascript:history.back()
  69. #
  70. # <%= url_for(action: 'index', controller: 'users') %>
  71. # # Assuming an "admin" namespace
  72. # # => /admin/users
  73. #
  74. # <%= url_for(action: 'index', controller: '/users') %>
  75. # # Specify absolute path with beginning slash
  76. # # => /users
  77. 1 def url_for(options = nil)
  78. case options
  79. when String
  80. options
  81. when nil
  82. super(only_path: _generate_paths_by_default)
  83. when Hash
  84. options = options.symbolize_keys
  85. ensure_only_path_option(options)
  86. super(options)
  87. when ActionController::Parameters
  88. ensure_only_path_option(options)
  89. super(options)
  90. when :back
  91. _back_url
  92. when Array
  93. components = options.dup
  94. options = components.extract_options!
  95. ensure_only_path_option(options)
  96. if options[:only_path]
  97. polymorphic_path(components, options)
  98. else
  99. polymorphic_url(components, options)
  100. end
  101. else
  102. method = _generate_paths_by_default ? :path : :url
  103. builder = ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.public_send(method)
  104. case options
  105. when Symbol
  106. builder.handle_string_call(self, options)
  107. when Class
  108. builder.handle_class_call(self, options)
  109. else
  110. builder.handle_model_call(self, options)
  111. end
  112. end
  113. end
  114. 1 def url_options #:nodoc:
  115. return super unless controller.respond_to?(:url_options)
  116. controller.url_options
  117. end
  118. 1 private
  119. 1 def _routes_context
  120. controller
  121. end
  122. 1 def optimize_routes_generation?
  123. controller.respond_to?(:optimize_routes_generation?, true) ?
  124. controller.optimize_routes_generation? : super
  125. end
  126. 1 def _generate_paths_by_default
  127. true
  128. end
  129. 1 def ensure_only_path_option(options)
  130. unless options.key?(:only_path)
  131. options[:only_path] = _generate_paths_by_default unless options[:host]
  132. end
  133. end
  134. end
  135. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template.rb

35.51% lines covered

107 relevant lines. 38 lines covered and 69 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "thread"
  3. 1 require "delegate"
  4. 1 module ActionView
  5. # = Action View Template
  6. 1 class Template
  7. 1 extend ActiveSupport::Autoload
  8. # === Encodings in ActionView::Template
  9. #
  10. # ActionView::Template is one of a few sources of potential
  11. # encoding issues in Rails. This is because the source for
  12. # templates are usually read from disk, and Ruby (like most
  13. # encoding-aware programming languages) assumes that the
  14. # String retrieved through File IO is encoded in the
  15. # <tt>default_external</tt> encoding. In Rails, the default
  16. # <tt>default_external</tt> encoding is UTF-8.
  17. #
  18. # As a result, if a user saves their template as ISO-8859-1
  19. # (for instance, using a non-Unicode-aware text editor),
  20. # and uses characters outside of the ASCII range, their
  21. # users will see diamonds with question marks in them in
  22. # the browser.
  23. #
  24. # For the rest of this documentation, when we say "UTF-8",
  25. # we mean "UTF-8 or whatever the default_internal encoding
  26. # is set to". By default, it will be UTF-8.
  27. #
  28. # To mitigate this problem, we use a few strategies:
  29. # 1. If the source is not valid UTF-8, we raise an exception
  30. # when the template is compiled to alert the user
  31. # to the problem.
  32. # 2. The user can specify the encoding using Ruby-style
  33. # encoding comments in any template engine. If such
  34. # a comment is supplied, Rails will apply that encoding
  35. # to the resulting compiled source returned by the
  36. # template handler.
  37. # 3. In all cases, we transcode the resulting String to
  38. # the UTF-8.
  39. #
  40. # This means that other parts of Rails can always assume
  41. # that templates are encoded in UTF-8, even if the original
  42. # source of the template was not UTF-8.
  43. #
  44. # From a user's perspective, the easiest thing to do is
  45. # to save your templates as UTF-8. If you do this, you
  46. # do not need to do anything else for things to "just work".
  47. #
  48. # === Instructions for template handlers
  49. #
  50. # The easiest thing for you to do is to simply ignore
  51. # encodings. Rails will hand you the template source
  52. # as the default_internal (generally UTF-8), raising
  53. # an exception for the user before sending the template
  54. # to you if it could not determine the original encoding.
  55. #
  56. # For the greatest simplicity, you can support only
  57. # UTF-8 as the <tt>default_internal</tt>. This means
  58. # that from the perspective of your handler, the
  59. # entire pipeline is just UTF-8.
  60. #
  61. # === Advanced: Handlers with alternate metadata sources
  62. #
  63. # If you want to provide an alternate mechanism for
  64. # specifying encodings (like ERB does via <%# encoding: ... %>),
  65. # you may indicate that you will handle encodings yourself
  66. # by implementing <tt>handles_encoding?</tt> on your handler.
  67. #
  68. # If you do, Rails will not try to encode the String
  69. # into the default_internal, passing you the unaltered
  70. # bytes tagged with the assumed encoding (from
  71. # default_external).
  72. #
  73. # In this case, make sure you return a String from
  74. # your handler encoded in the default_internal. Since
  75. # you are handling out-of-band metadata, you are
  76. # also responsible for alerting the user to any
  77. # problems with converting the user's data to
  78. # the <tt>default_internal</tt>.
  79. #
  80. # To do so, simply raise +WrongEncodingError+ as follows:
  81. #
  82. # raise WrongEncodingError.new(
  83. # problematic_string,
  84. # expected_encoding
  85. # )
  86. ##
  87. # :method: local_assigns
  88. #
  89. # Returns a hash with the defined local variables.
  90. #
  91. # Given this sub template rendering:
  92. #
  93. # <%= render "shared/header", { headline: "Welcome", person: person } %>
  94. #
  95. # You can use +local_assigns+ in the sub templates to access the local variables:
  96. #
  97. # local_assigns[:headline] # => "Welcome"
  98. 1 eager_autoload do
  99. 1 autoload :Error
  100. 1 autoload :RawFile
  101. 1 autoload :Renderable
  102. 1 autoload :Handlers
  103. 1 autoload :HTML
  104. 1 autoload :Inline
  105. 1 autoload :Sources
  106. 1 autoload :Text
  107. 1 autoload :Types
  108. end
  109. 1 extend Template::Handlers
  110. 1 attr_reader :identifier, :handler
  111. 1 attr_reader :variable, :format, :variant, :locals, :virtual_path
  112. 1 def initialize(source, identifier, handler, locals:, format: nil, variant: nil, virtual_path: nil)
  113. @source = source
  114. @identifier = identifier
  115. @handler = handler
  116. @compiled = false
  117. @locals = locals
  118. @virtual_path = virtual_path
  119. @variable = if @virtual_path
  120. base = @virtual_path.end_with?("/") ? "" : ::File.basename(@virtual_path)
  121. base =~ /\A_?(.*?)(?:\.\w+)*\z/
  122. $1.to_sym
  123. end
  124. @format = format
  125. @variant = variant
  126. @compile_mutex = Mutex.new
  127. end
  128. # Returns whether the underlying handler supports streaming. If so,
  129. # a streaming buffer *may* be passed when it starts rendering.
  130. 1 def supports_streaming?
  131. handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
  132. end
  133. # Render a template. If the template was not compiled yet, it is done
  134. # exactly before rendering.
  135. #
  136. # This method is instrumented as "!render_template.action_view". Notice that
  137. # we use a bang in this instrumentation because you don't want to
  138. # consume this in production. This is only slow if it's being listened to.
  139. 1 def render(view, locals, buffer = ActionView::OutputBuffer.new, add_to_stack: true, &block)
  140. instrument_render_template do
  141. compile!(view)
  142. view._run(method_name, self, locals, buffer, add_to_stack: add_to_stack, &block)
  143. end
  144. rescue => e
  145. handle_render_error(view, e)
  146. end
  147. 1 def type
  148. @type ||= Types[format]
  149. end
  150. 1 def short_identifier
  151. @short_identifier ||= defined?(Rails.root) ? identifier.delete_prefix("#{Rails.root}/") : identifier
  152. end
  153. 1 def inspect
  154. "#<#{self.class.name} #{short_identifier} locals=#{@locals.inspect}>"
  155. end
  156. 1 def source
  157. @source.to_s
  158. end
  159. # This method is responsible for properly setting the encoding of the
  160. # source. Until this point, we assume that the source is BINARY data.
  161. # If no additional information is supplied, we assume the encoding is
  162. # the same as <tt>Encoding.default_external</tt>.
  163. #
  164. # The user can also specify the encoding via a comment on the first
  165. # line of the template (# encoding: NAME-OF-ENCODING). This will work
  166. # with any template engine, as we process out the encoding comment
  167. # before passing the source on to the template engine, leaving a
  168. # blank line in its stead.
  169. 1 def encode!
  170. source = self.source
  171. return source unless source.encoding == Encoding::BINARY
  172. # Look for # encoding: *. If we find one, we'll encode the
  173. # String in that encoding, otherwise, we'll use the
  174. # default external encoding.
  175. if source.sub!(/\A#{ENCODING_FLAG}/, "")
  176. encoding = magic_encoding = $1
  177. else
  178. encoding = Encoding.default_external
  179. end
  180. # Tag the source with the default external encoding
  181. # or the encoding specified in the file
  182. source.force_encoding(encoding)
  183. # If the user didn't specify an encoding, and the handler
  184. # handles encodings, we simply pass the String as is to
  185. # the handler (with the default_external tag)
  186. if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
  187. source
  188. # Otherwise, if the String is valid in the encoding,
  189. # encode immediately to default_internal. This means
  190. # that if a handler doesn't handle encodings, it will
  191. # always get Strings in the default_internal
  192. elsif source.valid_encoding?
  193. source.encode!
  194. # Otherwise, since the String is invalid in the encoding
  195. # specified, raise an exception
  196. else
  197. raise WrongEncodingError.new(source, encoding)
  198. end
  199. end
  200. # Exceptions are marshalled when using the parallel test runner with DRb, so we need
  201. # to ensure that references to the template object can be marshalled as well. This means forgoing
  202. # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
  203. 1 def marshal_dump # :nodoc:
  204. [ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant ]
  205. end
  206. 1 def marshal_load(array) # :nodoc:
  207. @source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant = *array
  208. @compile_mutex = Mutex.new
  209. end
  210. 1 private
  211. # Compile a template. This method ensures a template is compiled
  212. # just once and removes the source after it is compiled.
  213. 1 def compile!(view)
  214. return if @compiled
  215. # Templates can be used concurrently in threaded environments
  216. # so compilation and any instance variable modification must
  217. # be synchronized
  218. @compile_mutex.synchronize do
  219. # Any thread holding this lock will be compiling the template needed
  220. # by the threads waiting. So re-check the @compiled flag to avoid
  221. # re-compilation
  222. return if @compiled
  223. mod = view.compiled_method_container
  224. instrument("!compile_template") do
  225. compile(mod)
  226. end
  227. @compiled = true
  228. end
  229. end
  230. # Among other things, this method is responsible for properly setting
  231. # the encoding of the compiled template.
  232. #
  233. # If the template engine handles encodings, we send the encoded
  234. # String to the engine without further processing. This allows
  235. # the template engine to support additional mechanisms for
  236. # specifying the encoding. For instance, ERB supports <%# encoding: %>
  237. #
  238. # Otherwise, after we figure out the correct encoding, we then
  239. # encode the source into <tt>Encoding.default_internal</tt>.
  240. # In general, this means that templates will be UTF-8 inside of Rails,
  241. # regardless of the original source encoding.
  242. 1 def compile(mod)
  243. source = encode!
  244. code = @handler.call(self, source)
  245. # Make sure that the resulting String to be eval'd is in the
  246. # encoding of the code
  247. original_source = source
  248. source = +<<-end_src
  249. def #{method_name}(local_assigns, output_buffer)
  250. @virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
  251. end
  252. end_src
  253. # Make sure the source is in the encoding of the returned code
  254. source.force_encoding(code.encoding)
  255. # In case we get back a String from a handler that is not in
  256. # BINARY or the default_internal, encode it to the default_internal
  257. source.encode!
  258. # Now, validate that the source we got back from the template
  259. # handler is valid in the default_internal. This is for handlers
  260. # that handle encoding but screw up
  261. unless source.valid_encoding?
  262. raise WrongEncodingError.new(source, Encoding.default_internal)
  263. end
  264. begin
  265. mod.module_eval(source, identifier, 0)
  266. rescue SyntaxError
  267. # Account for when code in the template is not syntactically valid; e.g. if we're using
  268. # ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
  269. # the result into the template, but missing an end parenthesis.
  270. raise SyntaxErrorInTemplate.new(self, original_source)
  271. end
  272. end
  273. 1 def handle_render_error(view, e)
  274. if e.is_a?(Template::Error)
  275. e.sub_template_of(self)
  276. raise e
  277. else
  278. raise Template::Error.new(self)
  279. end
  280. end
  281. 1 def locals_code
  282. # Only locals with valid variable names get set directly. Others will
  283. # still be available in local_assigns.
  284. locals = @locals - Module::RUBY_RESERVED_KEYWORDS
  285. locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
  286. # Assign for the same variable is to suppress unused variable warning
  287. locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
  288. end
  289. 1 def method_name
  290. @method_name ||= begin
  291. m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
  292. m.tr!("-", "_")
  293. m
  294. end
  295. end
  296. 1 def identifier_method_name
  297. short_identifier.tr("^a-z_", "_")
  298. end
  299. 1 def instrument(action, &block) # :doc:
  300. ActiveSupport::Notifications.instrument("#{action}.action_view", instrument_payload, &block)
  301. end
  302. 1 def instrument_render_template(&block)
  303. ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block)
  304. end
  305. 1 def instrument_payload
  306. { virtual_path: @virtual_path, identifier: @identifier }
  307. end
  308. end
  309. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers.rb

80.56% lines covered

36 relevant lines. 29 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView #:nodoc:
  3. # = Action View Template Handlers
  4. 1 class Template #:nodoc:
  5. 1 module Handlers #:nodoc:
  6. 1 autoload :Raw, "action_view/template/handlers/raw"
  7. 1 autoload :ERB, "action_view/template/handlers/erb"
  8. 1 autoload :Html, "action_view/template/handlers/html"
  9. 1 autoload :Builder, "action_view/template/handlers/builder"
  10. 1 def self.extended(base)
  11. 1 base.register_default_template_handler :raw, Raw.new
  12. 1 base.register_template_handler :erb, ERB.new
  13. 1 base.register_template_handler :html, Html.new
  14. 1 base.register_template_handler :builder, Builder.new
  15. 1 base.register_template_handler :ruby, lambda { |_, source| source }
  16. end
  17. 1 @@template_handlers = {}
  18. 1 @@default_template_handlers = nil
  19. 1 def self.extensions
  20. 1 @@template_extensions ||= @@template_handlers.keys
  21. end
  22. # Register an object that knows how to handle template files with the given
  23. # extensions. This can be used to implement new template types.
  24. # The handler must respond to +:call+, which will be passed the template
  25. # and should return the rendered template as a String.
  26. 1 def register_template_handler(*extensions, handler)
  27. 5 raise(ArgumentError, "Extension is required") if extensions.empty?
  28. 5 extensions.each do |extension|
  29. 5 @@template_handlers[extension.to_sym] = handler
  30. end
  31. 5 @@template_extensions = nil
  32. end
  33. # Opposite to register_template_handler.
  34. 1 def unregister_template_handler(*extensions)
  35. extensions.each do |extension|
  36. handler = @@template_handlers.delete extension.to_sym
  37. @@default_template_handlers = nil if @@default_template_handlers == handler
  38. end
  39. @@template_extensions = nil
  40. end
  41. 1 def template_handler_extensions
  42. @@template_handlers.keys.map(&:to_s).sort
  43. end
  44. 1 def registered_template_handler(extension)
  45. extension && @@template_handlers[extension.to_sym]
  46. end
  47. 1 def register_default_template_handler(extension, klass)
  48. 1 register_template_handler(extension, klass)
  49. 1 @@default_template_handlers = klass
  50. end
  51. 1 def handler_for_extension(extension)
  52. registered_template_handler(extension) || @@default_template_handlers
  53. end
  54. end
  55. end
  56. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/builder.rb

58.33% lines covered

12 relevant lines. 7 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. 1 module Template::Handlers
  4. 1 class Builder
  5. 1 class_attribute :default_format, default: :xml
  6. 1 def call(template, source)
  7. require_engine
  8. "xml = ::Builder::XmlMarkup.new(:indent => 2);" \
  9. "self.output_buffer = xml.target!;" +
  10. source +
  11. ";xml.target!;"
  12. end
  13. 1 private
  14. 1 def require_engine # :doc:
  15. @required ||= begin
  16. require "builder"
  17. true
  18. end
  19. end
  20. end
  21. end
  22. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/erb.rb

46.88% lines covered

32 relevant lines. 15 lines covered and 17 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. 1 class Template
  4. 1 module Handlers
  5. 1 class ERB
  6. 1 autoload :Erubi, "action_view/template/handlers/erb/erubi"
  7. # Specify trim mode for the ERB compiler. Defaults to '-'.
  8. # See ERB documentation for suitable values.
  9. 1 class_attribute :erb_trim_mode, default: "-"
  10. # Default implementation used.
  11. 1 class_attribute :erb_implementation, default: Erubi
  12. # Do not escape templates of these mime types.
  13. 1 class_attribute :escape_ignore_list, default: ["text/plain"]
  14. 1 ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
  15. 1 def self.call(template, source)
  16. new.call(template, source)
  17. end
  18. 1 def supports_streaming?
  19. true
  20. end
  21. 1 def handles_encoding?
  22. true
  23. end
  24. 1 def call(template, source)
  25. # First, convert to BINARY, so in case the encoding is
  26. # wrong, we can still find an encoding tag
  27. # (<%# encoding %>) inside the String using a regular
  28. # expression
  29. template_source = source.b
  30. erb = template_source.gsub(ENCODING_TAG, "")
  31. encoding = $2
  32. erb.force_encoding valid_encoding(source.dup, encoding)
  33. # Always make sure we return a String in the default_internal
  34. erb.encode!
  35. options = {
  36. escape: (self.class.escape_ignore_list.include? template.type),
  37. trim: (self.class.erb_trim_mode == "-")
  38. }
  39. if ActionView::Base.annotate_rendered_view_with_filenames && template.format == :html
  40. options[:preamble] = "@output_buffer.safe_append='<!-- BEGIN #{template.short_identifier} -->';"
  41. options[:postamble] = "@output_buffer.safe_append='<!-- END #{template.short_identifier} -->';@output_buffer.to_s"
  42. end
  43. self.class.erb_implementation.new(erb, options).src
  44. end
  45. 1 private
  46. 1 def valid_encoding(string, encoding)
  47. # If a magic encoding comment was found, tag the
  48. # String with this encoding. This is for a case
  49. # where the original String was assumed to be,
  50. # for instance, UTF-8, but a magic comment
  51. # proved otherwise
  52. string.force_encoding(encoding) if encoding
  53. # If the String is valid, return the encoding we found
  54. return string.encoding if string.valid_encoding?
  55. # Otherwise, raise an exception
  56. raise WrongEncodingError.new(string, string.encoding)
  57. end
  58. end
  59. end
  60. end
  61. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/erb/erubi.rb

30.61% lines covered

49 relevant lines. 15 lines covered and 34 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "erubi"
  3. 1 module ActionView
  4. 1 class Template
  5. 1 module Handlers
  6. 1 class ERB
  7. 1 class Erubi < ::Erubi::Engine
  8. # :nodoc: all
  9. 1 def initialize(input, properties = {})
  10. @newline_pending = 0
  11. # Dup properties so that we don't modify argument
  12. properties = Hash[properties]
  13. properties[:bufvar] ||= "@output_buffer"
  14. properties[:preamble] ||= ""
  15. properties[:postamble] ||= "#{properties[:bufvar]}.to_s"
  16. properties[:escapefunc] = ""
  17. super
  18. end
  19. 1 def evaluate(action_view_erb_handler_context)
  20. src = @src
  21. view = Class.new(ActionView::Base) {
  22. include action_view_erb_handler_context._routes.url_helpers
  23. class_eval("define_method(:_template) { |local_assigns, output_buffer| #{src} }", defined?(@filename) ? @filename : "(erubi)", 0)
  24. }.empty
  25. view._run(:_template, nil, {}, ActionView::OutputBuffer.new)
  26. end
  27. 1 private
  28. 1 def add_text(text)
  29. return if text.empty?
  30. if text == "\n"
  31. @newline_pending += 1
  32. else
  33. src << bufvar << ".safe_append='"
  34. src << "\n" * @newline_pending if @newline_pending > 0
  35. src << text.gsub(/['\\]/, '\\\\\&')
  36. src << "'.freeze;"
  37. @newline_pending = 0
  38. end
  39. end
  40. 1 BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
  41. 1 def add_expression(indicator, code)
  42. flush_newline_if_pending(src)
  43. if (indicator == "==") || @escape
  44. src << bufvar << ".safe_expr_append="
  45. else
  46. src << bufvar << ".append="
  47. end
  48. if BLOCK_EXPR.match?(code)
  49. src << " " << code
  50. else
  51. src << "(" << code << ");"
  52. end
  53. end
  54. 1 def add_code(code)
  55. flush_newline_if_pending(src)
  56. super
  57. end
  58. 1 def add_postamble(_)
  59. flush_newline_if_pending(src)
  60. super
  61. end
  62. 1 def flush_newline_if_pending(src)
  63. if @newline_pending > 0
  64. src << bufvar << ".safe_append='#{"\n" * @newline_pending}'.freeze;"
  65. @newline_pending = 0
  66. end
  67. end
  68. end
  69. end
  70. end
  71. end
  72. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/html.rb

80.0% lines covered

5 relevant lines. 4 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. 1 module Template::Handlers
  4. 1 class Html < Raw
  5. 1 def call(template, source)
  6. "ActionView::OutputBuffer.new #{super}"
  7. end
  8. end
  9. end
  10. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/handlers/raw.rb

80.0% lines covered

5 relevant lines. 4 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. 1 module Template::Handlers
  4. 1 class Raw
  5. 1 def call(template, source)
  6. "#{source.inspect}.html_safe;"
  7. end
  8. end
  9. end
  10. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/resolver.rb

47.39% lines covered

211 relevant lines. 100 lines covered and 111 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "pathname"
  3. 1 require "active_support/core_ext/class"
  4. 1 require "active_support/core_ext/module/attribute_accessors"
  5. 1 require "action_view/template"
  6. 1 require "thread"
  7. 1 require "concurrent/map"
  8. 1 module ActionView
  9. # = Action View Resolver
  10. 1 class Resolver
  11. # Keeps all information about view path and builds virtual path.
  12. 1 class Path
  13. 1 attr_reader :name, :prefix, :partial, :virtual
  14. 1 alias_method :partial?, :partial
  15. 1 def self.build(name, prefix, partial)
  16. virtual = +""
  17. virtual << "#{prefix}/" unless prefix.empty?
  18. virtual << (partial ? "_#{name}" : name)
  19. new name, prefix, partial, virtual
  20. end
  21. 1 def initialize(name, prefix, partial, virtual)
  22. @name = name
  23. @prefix = prefix
  24. @partial = partial
  25. @virtual = virtual
  26. end
  27. 1 def to_str
  28. @virtual
  29. end
  30. 1 alias :to_s :to_str
  31. end
  32. 1 class PathParser # :nodoc:
  33. 1 def build_path_regex
  34. handlers = Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
  35. formats = Template::Types.symbols.map { |x| Regexp.escape(x) }.join("|")
  36. locales = "[a-z]{2}(?:-[A-Z]{2})?"
  37. variants = "[^.]*"
  38. %r{
  39. \A
  40. (?:(?<prefix>.*)/)?
  41. (?<partial>_)?
  42. (?<action>.*?)
  43. (?:\.(?<locale>#{locales}))??
  44. (?:\.(?<format>#{formats}))??
  45. (?:\+(?<variant>#{variants}))??
  46. (?:\.(?<handler>#{handlers}))?
  47. \z
  48. }x
  49. end
  50. 1 def parse(path)
  51. @regex ||= build_path_regex
  52. match = @regex.match(path)
  53. {
  54. prefix: match[:prefix] || "",
  55. action: match[:action],
  56. partial: !!match[:partial],
  57. locale: match[:locale]&.to_sym,
  58. handler: match[:handler]&.to_sym,
  59. format: match[:format]&.to_sym,
  60. variant: match[:variant]
  61. }
  62. end
  63. end
  64. # Threadsafe template cache
  65. 1 class Cache #:nodoc:
  66. 1 class SmallCache < Concurrent::Map
  67. 1 def initialize(options = {})
  68. 22 super(options.merge(initial_capacity: 2))
  69. end
  70. end
  71. # Preallocate all the default blocks for performance/memory consumption reasons
  72. 1 PARTIAL_BLOCK = lambda { |cache, partial| cache[partial] = SmallCache.new }
  73. 1 PREFIX_BLOCK = lambda { |cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK) }
  74. 1 NAME_BLOCK = lambda { |cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK) }
  75. 1 KEY_BLOCK = lambda { |cache, key| cache[key] = SmallCache.new(&NAME_BLOCK) }
  76. # Usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
  77. 1 NO_TEMPLATES = [].freeze
  78. 1 def initialize
  79. 11 @data = SmallCache.new(&KEY_BLOCK)
  80. 11 @query_cache = SmallCache.new
  81. end
  82. 1 def inspect
  83. "#{to_s[0..-2]} keys=#{@data.size} queries=#{@query_cache.size}>"
  84. end
  85. # Cache the templates returned by the block
  86. 1 def cache(key, name, prefix, partial, locals)
  87. @data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
  88. end
  89. 1 def cache_query(query) # :nodoc:
  90. @query_cache[query] ||= canonical_no_templates(yield)
  91. end
  92. 1 def clear
  93. 22 @data.clear
  94. 22 @query_cache.clear
  95. end
  96. # Get the cache size. Do not call this
  97. # method. This method is not guaranteed to be here ever.
  98. 1 def size # :nodoc:
  99. size = 0
  100. @data.each_value do |v1|
  101. v1.each_value do |v2|
  102. v2.each_value do |v3|
  103. v3.each_value do |v4|
  104. size += v4.size
  105. end
  106. end
  107. end
  108. end
  109. size + @query_cache.size
  110. end
  111. 1 private
  112. 1 def canonical_no_templates(templates)
  113. templates.empty? ? NO_TEMPLATES : templates
  114. end
  115. end
  116. 1 cattr_accessor :caching, default: true
  117. 1 class << self
  118. 1 alias :caching? :caching
  119. end
  120. 1 def initialize
  121. 11 @cache = Cache.new
  122. end
  123. 1 def clear_cache
  124. 22 @cache.clear
  125. end
  126. # Normalizes the arguments and passes it on to find_templates.
  127. 1 def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
  128. locals = locals.map(&:to_s).sort!.freeze
  129. cached(key, [name, prefix, partial], details, locals) do
  130. _find_all(name, prefix, partial, details, key, locals)
  131. end
  132. end
  133. 1 def find_all_with_query(query) # :nodoc:
  134. @cache.cache_query(query) { find_template_paths(File.join(@path, query)) }
  135. end
  136. 1 private
  137. 1 def _find_all(name, prefix, partial, details, key, locals)
  138. find_templates(name, prefix, partial, details, locals)
  139. end
  140. 1 delegate :caching?, to: :class
  141. # This is what child classes implement. No defaults are needed
  142. # because Resolver guarantees that the arguments are present and
  143. # normalized.
  144. 1 def find_templates(name, prefix, partial, details, locals = [])
  145. raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, locals = []) method"
  146. end
  147. # Handles templates caching. If a key is given and caching is on
  148. # always check the cache before hitting the resolver. Otherwise,
  149. # it always hits the resolver but if the key is present, check if the
  150. # resolver is fresher before returning it.
  151. 1 def cached(key, path_info, details, locals)
  152. name, prefix, partial = path_info
  153. if key
  154. @cache.cache(key, name, prefix, partial, locals) do
  155. yield
  156. end
  157. else
  158. yield
  159. end
  160. end
  161. end
  162. # An abstract class that implements a Resolver with path semantics.
  163. 1 class PathResolver < Resolver #:nodoc:
  164. 1 EXTENSIONS = { locale: ".", formats: ".", variants: "+", handlers: "." }
  165. 1 DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
  166. 1 def initialize
  167. 11 @pattern = DEFAULT_PATTERN
  168. 11 @unbound_templates = Concurrent::Map.new
  169. 11 @path_parser = PathParser.new
  170. 11 super
  171. end
  172. 1 def clear_cache
  173. 22 @unbound_templates.clear
  174. 22 @path_parser = PathParser.new
  175. 22 super
  176. end
  177. 1 private
  178. 1 def _find_all(name, prefix, partial, details, key, locals)
  179. path = Path.build(name, prefix, partial)
  180. query(path, details, details[:formats], locals, cache: !!key)
  181. end
  182. 1 def query(path, details, formats, locals, cache:)
  183. template_paths = find_template_paths_from_details(path, details)
  184. template_paths = reject_files_external_to_app(template_paths)
  185. template_paths.map do |template|
  186. unbound_template =
  187. if cache
  188. @unbound_templates.compute_if_absent([template, path.virtual]) do
  189. build_unbound_template(template, path.virtual)
  190. end
  191. else
  192. build_unbound_template(template, path.virtual)
  193. end
  194. unbound_template.bind_locals(locals)
  195. end
  196. end
  197. 1 def source_for_template(template)
  198. Template::Sources::File.new(template)
  199. end
  200. 1 def build_unbound_template(template, virtual_path)
  201. handler, format, variant = extract_handler_and_format_and_variant(template)
  202. source = source_for_template(template)
  203. UnboundTemplate.new(
  204. source,
  205. template,
  206. handler,
  207. virtual_path: virtual_path,
  208. format: format,
  209. variant: variant,
  210. )
  211. end
  212. 1 def reject_files_external_to_app(files)
  213. files.reject { |filename| !inside_path?(@path, filename) }
  214. end
  215. 1 def find_template_paths_from_details(path, details)
  216. if path.name.include?(".")
  217. ActiveSupport::Deprecation.warn("Rendering actions with '.' in the name is deprecated: #{path}")
  218. end
  219. query = build_query(path, details)
  220. find_template_paths(query)
  221. end
  222. 1 def find_template_paths(query)
  223. Dir[query].uniq.reject do |filename|
  224. File.directory?(filename) ||
  225. # deals with case-insensitive file systems.
  226. !File.fnmatch(query, filename, File::FNM_EXTGLOB)
  227. end
  228. end
  229. 1 def inside_path?(path, filename)
  230. filename = File.expand_path(filename)
  231. path = File.join(path, "")
  232. filename.start_with?(path)
  233. end
  234. # Helper for building query glob string based on resolver's pattern.
  235. 1 def build_query(path, details)
  236. query = @pattern.dup
  237. prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
  238. query.gsub!(/:prefix(\/)?/, prefix)
  239. partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
  240. query.gsub!(":action", partial)
  241. details.each do |ext, candidates|
  242. if ext == :variants && candidates == :any
  243. query.gsub!(/:#{ext}/, "*")
  244. else
  245. query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")
  246. end
  247. end
  248. File.expand_path(query, @path)
  249. end
  250. 1 def escape_entry(entry)
  251. entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
  252. end
  253. # Extract handler, formats and variant from path. If a format cannot be found neither
  254. # from the path, or the handler, we should return the array of formats given
  255. # to the resolver.
  256. 1 def extract_handler_and_format_and_variant(path)
  257. details = @path_parser.parse(path)
  258. handler = Template.handler_for_extension(details[:handler])
  259. format = details[:format] || handler.try(:default_format)
  260. variant = details[:variant]
  261. # Template::Types[format] and handler.default_format can return nil
  262. [handler, format, variant]
  263. end
  264. end
  265. # A resolver that loads files from the filesystem.
  266. 1 class FileSystemResolver < PathResolver
  267. 1 attr_reader :path
  268. 1 def initialize(path)
  269. 11 raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
  270. 11 super()
  271. 11 @path = File.expand_path(path)
  272. end
  273. 1 def to_s
  274. @path.to_s
  275. end
  276. 1 alias :to_path :to_s
  277. 1 def eql?(resolver)
  278. self.class.equal?(resolver.class) && to_path == resolver.to_path
  279. end
  280. 1 alias :== :eql?
  281. end
  282. # An Optimized resolver for Rails' most common case.
  283. 1 class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
  284. 1 def initialize(path)
  285. 9 super(path)
  286. end
  287. 1 private
  288. 1 def find_candidate_template_paths(path)
  289. # Instead of checking for every possible path, as our other globs would
  290. # do, scan the directory for files with the right prefix.
  291. query = "#{escape_entry(File.join(@path, path))}*"
  292. Dir[query].reject do |filename|
  293. File.directory?(filename)
  294. end
  295. end
  296. 1 def find_template_paths_from_details(path, details)
  297. if path.name.include?(".")
  298. # Fall back to the unoptimized resolver, which will warn
  299. return super
  300. end
  301. candidates = find_candidate_template_paths(path)
  302. regex = build_regex(path, details)
  303. candidates.uniq.reject do |filename|
  304. # This regex match does double duty of finding only files which match
  305. # details (instead of just matching the prefix) and also filtering for
  306. # case-insensitive file systems.
  307. !regex.match?(filename) ||
  308. File.directory?(filename)
  309. end.sort_by do |filename|
  310. # Because we scanned the directory, instead of checking for files
  311. # one-by-one, they will be returned in an arbitrary order.
  312. # We can use the matches found by the regex and sort by their index in
  313. # details.
  314. match = filename.match(regex)
  315. EXTENSIONS.keys.map do |ext|
  316. if ext == :variants && details[ext] == :any
  317. match[ext].nil? ? 0 : 1
  318. elsif match[ext].nil?
  319. # No match should be last
  320. details[ext].length
  321. else
  322. found = match[ext].to_sym
  323. details[ext].index(found)
  324. end
  325. end
  326. end
  327. end
  328. 1 def build_regex(path, details)
  329. query = Regexp.escape(File.join(@path, path))
  330. exts = EXTENSIONS.map do |ext, prefix|
  331. match =
  332. if ext == :variants && details[ext] == :any
  333. ".*?"
  334. else
  335. arr = details[ext].compact
  336. arr.uniq!
  337. arr.map! { |e| Regexp.escape(e) }
  338. arr.join("|")
  339. end
  340. prefix = Regexp.escape(prefix)
  341. "(#{prefix}(?<#{ext}>#{match}))?"
  342. end.join
  343. %r{\A#{query}#{exts}\z}
  344. end
  345. end
  346. # The same as FileSystemResolver but does not allow templates to store
  347. # a virtual path since it is invalid for such resolvers.
  348. 1 class FallbackFileSystemResolver < FileSystemResolver #:nodoc:
  349. 1 private_class_method :new
  350. 1 def self.instances
  351. 1 [new(""), new("/")]
  352. end
  353. 1 def build_unbound_template(template, _)
  354. super(template, nil)
  355. end
  356. 1 def reject_files_external_to_app(files)
  357. files
  358. end
  359. end
  360. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/template/types.rb

72.41% lines covered

29 relevant lines. 21 lines covered and 8 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/module/attribute_accessors"
  3. 1 module ActionView
  4. 1 class Template #:nodoc:
  5. 1 class Types
  6. 1 class Type
  7. 1 SET = Struct.new(:symbols).new([ :html, :text, :js, :css, :xml, :json ])
  8. 1 def self.[](type)
  9. if type.is_a?(self)
  10. type
  11. else
  12. new(type)
  13. end
  14. end
  15. 1 attr_reader :symbol
  16. 1 def initialize(symbol)
  17. @symbol = symbol.to_sym
  18. end
  19. 1 def to_s
  20. @symbol.to_s
  21. end
  22. 1 alias to_str to_s
  23. 1 def ref
  24. @symbol
  25. end
  26. 1 alias to_sym ref
  27. 1 def ==(type)
  28. @symbol == type.to_sym unless type.blank?
  29. end
  30. end
  31. 1 cattr_accessor :type_klass
  32. 1 def self.delegate_to(klass)
  33. 3 self.type_klass = klass
  34. end
  35. 1 delegate_to Type
  36. 1 def self.[](type)
  37. type_klass[type]
  38. end
  39. 1 def self.symbols
  40. 1 type_klass::SET.symbols
  41. end
  42. end
  43. end
  44. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/test_case.rb

50.0% lines covered

134 relevant lines. 67 lines covered and 67 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/module/redefine_method"
  3. 1 require "action_controller"
  4. 1 require "action_controller/test_case"
  5. 1 require "action_view"
  6. 1 require "rails-dom-testing"
  7. 1 module ActionView
  8. # = Action View Test Case
  9. 1 class TestCase < ActiveSupport::TestCase
  10. 1 class TestController < ActionController::Base
  11. 1 include ActionDispatch::TestProcess
  12. 1 attr_accessor :request, :response, :params
  13. 1 class << self
  14. # Overrides AbstractController::Base#controller_path
  15. 1 attr_accessor :controller_path
  16. end
  17. 1 def controller_path=(path)
  18. self.class.controller_path = path
  19. end
  20. 1 def initialize
  21. super
  22. self.class.controller_path = ""
  23. @request = ActionController::TestRequest.create(self.class)
  24. @response = ActionDispatch::TestResponse.new
  25. @request.env.delete("PATH_INFO")
  26. @params = ActionController::Parameters.new
  27. end
  28. end
  29. 1 module Behavior
  30. 1 extend ActiveSupport::Concern
  31. 1 include ActionDispatch::Assertions, ActionDispatch::TestProcess
  32. 1 include Rails::Dom::Testing::Assertions
  33. 1 include ActionController::TemplateAssertions
  34. 1 include ActionView::Context
  35. 1 include ActionDispatch::Routing::PolymorphicRoutes
  36. 1 include AbstractController::Helpers
  37. 1 include ActionView::Helpers
  38. 1 include ActionView::RecordIdentifier
  39. 1 include ActionView::RoutingUrlFor
  40. 1 include ActiveSupport::Testing::ConstantLookup
  41. 1 delegate :lookup_context, to: :controller
  42. 1 attr_accessor :controller, :output_buffer, :rendered
  43. 1 module ClassMethods
  44. 1 def tests(helper_class)
  45. case helper_class
  46. when String, Symbol
  47. self.helper_class = "#{helper_class.to_s.underscore}_helper".camelize.safe_constantize
  48. when Module
  49. self.helper_class = helper_class
  50. end
  51. end
  52. 1 def determine_default_helper_class(name)
  53. determine_constant_from_test_name(name) do |constant|
  54. Module === constant && !(Class === constant)
  55. end
  56. end
  57. 1 def helper_method(*methods)
  58. # Almost a duplicate from ActionController::Helpers
  59. methods.flatten.each do |method|
  60. _helpers_for_modification.module_eval <<-end_eval, __FILE__, __LINE__ + 1
  61. def #{method}(*args, &block) # def current_user(*args, &block)
  62. _test_case.send(:'#{method}', *args, &block) # _test_case.send(:'current_user', *args, &block)
  63. end # end
  64. ruby2_keywords(:'#{method}') if respond_to?(:ruby2_keywords, true)
  65. end_eval
  66. end
  67. end
  68. 1 attr_writer :helper_class
  69. 1 def helper_class
  70. @helper_class ||= determine_default_helper_class(name)
  71. end
  72. 1 def new(*)
  73. include_helper_modules!
  74. super
  75. end
  76. 1 private
  77. 1 def include_helper_modules!
  78. helper(helper_class) if helper_class
  79. include _helpers
  80. end
  81. end
  82. 1 def setup_with_controller
  83. controller_class = Class.new(ActionView::TestCase::TestController)
  84. @controller = controller_class.new
  85. @request = @controller.request
  86. @view_flow = ActionView::OutputFlow.new
  87. # empty string ensures buffer has UTF-8 encoding as
  88. # new without arguments returns ASCII-8BIT encoded buffer like String#new
  89. @output_buffer = ActiveSupport::SafeBuffer.new ""
  90. @rendered = +""
  91. test_case_instance = self
  92. controller_class.define_method(:_test_case) { test_case_instance }
  93. end
  94. 1 def config
  95. @controller.config if @controller.respond_to?(:config)
  96. end
  97. 1 def render(options = {}, local_assigns = {}, &block)
  98. view.assign(view_assigns)
  99. @rendered << output = view.render(options, local_assigns, &block)
  100. output
  101. end
  102. 1 def rendered_views
  103. @_rendered_views ||= RenderedViewsCollection.new
  104. end
  105. 1 def _routes
  106. @controller._routes if @controller.respond_to?(:_routes)
  107. end
  108. # Need to experiment if this priority is the best one: rendered => output_buffer
  109. 1 class RenderedViewsCollection
  110. 1 def initialize
  111. @rendered_views ||= Hash.new { |hash, key| hash[key] = [] }
  112. end
  113. 1 def add(view, locals)
  114. @rendered_views[view] ||= []
  115. @rendered_views[view] << locals
  116. end
  117. 1 def locals_for(view)
  118. @rendered_views[view]
  119. end
  120. 1 def rendered_views
  121. @rendered_views.keys
  122. end
  123. 1 def view_rendered?(view, expected_locals)
  124. locals_for(view).any? do |actual_locals|
  125. expected_locals.all? { |key, value| value == actual_locals[key] }
  126. end
  127. end
  128. end
  129. 1 included do
  130. 1 setup :setup_with_controller
  131. 1 ActiveSupport.run_load_hooks(:action_view_test_case, self)
  132. 1 helper do
  133. 1 def protect_against_forgery?
  134. false
  135. end
  136. 1 def _test_case
  137. controller._test_case
  138. end
  139. end
  140. end
  141. 1 private
  142. # Need to experiment if this priority is the best one: rendered => output_buffer
  143. 1 def document_root_element
  144. Nokogiri::HTML::Document.parse(@rendered.blank? ? @output_buffer : @rendered).root
  145. end
  146. 1 module Locals
  147. 1 attr_accessor :rendered_views
  148. 1 def render(options = {}, local_assigns = {})
  149. case options
  150. when Hash
  151. if block_given?
  152. rendered_views.add options[:layout], options[:locals]
  153. elsif options.key?(:partial)
  154. rendered_views.add options[:partial], options[:locals]
  155. end
  156. else
  157. rendered_views.add options, local_assigns
  158. end
  159. super
  160. end
  161. end
  162. # The instance of ActionView::Base that is used by +render+.
  163. 1 def view
  164. @view ||= begin
  165. view = @controller.view_context
  166. view.singleton_class.include(_helpers)
  167. view.extend(Locals)
  168. view.rendered_views = rendered_views
  169. view.output_buffer = output_buffer
  170. view
  171. end
  172. end
  173. 1 alias_method :_view, :view
  174. 1 INTERNAL_IVARS = [
  175. :@NAME,
  176. :@failures,
  177. :@assertions,
  178. :@__io__,
  179. :@_assertion_wrapped,
  180. :@_assertions,
  181. :@_result,
  182. :@_routes,
  183. :@controller,
  184. :@_layouts,
  185. :@_files,
  186. :@_rendered_views,
  187. :@method_name,
  188. :@output_buffer,
  189. :@_partials,
  190. :@passed,
  191. :@rendered,
  192. :@request,
  193. :@routes,
  194. :@tagged_logger,
  195. :@_templates,
  196. :@options,
  197. :@test_passed,
  198. :@view,
  199. :@view_context_class,
  200. :@view_flow,
  201. :@_subscribers,
  202. :@html_document
  203. ]
  204. 1 def _user_defined_ivars
  205. instance_variables - INTERNAL_IVARS
  206. end
  207. # Returns a Hash of instance variables and their values, as defined by
  208. # the user in the test case, which are then assigned to the view being
  209. # rendered. This is generally intended for internal use and extension
  210. # frameworks.
  211. 1 def view_assigns
  212. Hash[_user_defined_ivars.map do |ivar|
  213. [ivar[1..-1].to_sym, instance_variable_get(ivar)]
  214. end]
  215. end
  216. 1 def method_missing(selector, *args)
  217. begin
  218. routes = @controller.respond_to?(:_routes) && @controller._routes
  219. rescue
  220. # Don't call routes, if there is an error on _routes call
  221. end
  222. if routes &&
  223. (routes.named_routes.route_defined?(selector) ||
  224. routes.mounted_helpers.method_defined?(selector))
  225. @controller.__send__(selector, *args)
  226. else
  227. super
  228. end
  229. end
  230. 1 def respond_to_missing?(name, include_private = false)
  231. begin
  232. routes = defined?(@controller) && @controller.respond_to?(:_routes) && @controller._routes
  233. rescue
  234. # Don't call routes, if there is an error on _routes call
  235. end
  236. routes &&
  237. (routes.named_routes.route_defined?(name) ||
  238. routes.mounted_helpers.method_defined?(name))
  239. end
  240. end
  241. 1 include Behavior
  242. end
  243. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/testing/resolvers.rb

50.0% lines covered

22 relevant lines. 11 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "action_view/template/resolver"
  3. 1 module ActionView #:nodoc:
  4. # Use FixtureResolver in your tests to simulate the presence of files on the
  5. # file system. This is used internally by Rails' own test suite, and is
  6. # useful for testing extensions that have no way of knowing what the file
  7. # system will look like at runtime.
  8. 1 class FixtureResolver < OptimizedFileSystemResolver
  9. 1 def initialize(hash = {})
  10. super("")
  11. @hash = hash
  12. @path = ""
  13. end
  14. 1 def data
  15. @hash
  16. end
  17. 1 def to_s
  18. @hash.keys.join(", ")
  19. end
  20. 1 private
  21. 1 def find_candidate_template_paths(path)
  22. @hash.keys.select do |fixture|
  23. fixture.start_with?(path.virtual)
  24. end.map do |fixture|
  25. "/#{fixture}"
  26. end
  27. end
  28. 1 def source_for_template(template)
  29. @hash[template[1..template.size]]
  30. end
  31. end
  32. 1 class NullResolver < PathResolver
  33. 1 def query(path, exts, _, locals, cache:)
  34. handler, format, variant = extract_handler_and_format_and_variant(path)
  35. [ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: format, variant: variant, locals: locals)]
  36. end
  37. end
  38. end

vender/bundle/ruby/2.6.0/gems/actionview-6.1.3/lib/action_view/view_paths.rb

90.7% lines covered

43 relevant lines. 39 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActionView
  3. 1 module ViewPaths
  4. 1 extend ActiveSupport::Concern
  5. 1 included do
  6. 3 ViewPaths.set_view_paths(self, ActionView::PathSet.new.freeze)
  7. end
  8. 1 delegate :template_exists?, :any_templates?, :view_paths, :formats, :formats=,
  9. :locale, :locale=, to: :lookup_context
  10. 1 module ClassMethods
  11. 1 def _view_paths
  12. 10 ViewPaths.get_view_paths(self)
  13. end
  14. 1 def _view_paths=(paths)
  15. 9 ViewPaths.set_view_paths(self, paths)
  16. end
  17. 1 def _prefixes # :nodoc:
  18. 2 @_prefixes ||= begin
  19. 2 return local_prefixes if superclass.abstract?
  20. 1 local_prefixes + superclass._prefixes
  21. end
  22. end
  23. # Append a path to the list of view paths for this controller.
  24. #
  25. # ==== Parameters
  26. # * <tt>path</tt> - If a String is provided, it gets converted into
  27. # the default view path. You may also provide a custom view path
  28. # (see ActionView::PathSet for more information)
  29. 1 def append_view_path(path)
  30. self._view_paths = view_paths + Array(path)
  31. end
  32. # Prepend a path to the list of view paths for this controller.
  33. #
  34. # ==== Parameters
  35. # * <tt>path</tt> - If a String is provided, it gets converted into
  36. # the default view path. You may also provide a custom view path
  37. # (see ActionView::PathSet for more information)
  38. 1 def prepend_view_path(path)
  39. 9 self._view_paths = ActionView::PathSet.new(Array(path) + view_paths)
  40. end
  41. # A list of all of the default view paths for this controller.
  42. 1 def view_paths
  43. 9 _view_paths
  44. end
  45. # Set the view paths.
  46. #
  47. # ==== Parameters
  48. # * <tt>paths</tt> - If a PathSet is provided, use that;
  49. # otherwise, process the parameter into a PathSet.
  50. 1 def view_paths=(paths)
  51. self._view_paths = ActionView::PathSet.new(Array(paths))
  52. end
  53. 1 private
  54. # Override this method in your controller if you want to change paths prefixes for finding views.
  55. # Prefixes defined here will still be added to parents' <tt>._prefixes</tt>.
  56. 1 def local_prefixes
  57. 2 [controller_path]
  58. end
  59. end
  60. # :stopdoc:
  61. 1 @all_view_paths = {}
  62. 1 def self.get_view_paths(klass)
  63. 12 @all_view_paths[klass] || get_view_paths(klass.superclass)
  64. end
  65. 1 def self.set_view_paths(klass, paths)
  66. 12 @all_view_paths[klass] = paths
  67. end
  68. 1 def self.all_view_paths
  69. 2 @all_view_paths.values.uniq
  70. end
  71. # :startdoc:
  72. # The prefixes used in render "foo" shortcuts.
  73. 1 def _prefixes # :nodoc:
  74. 1 self.class._prefixes
  75. end
  76. # <tt>LookupContext</tt> is the object responsible for holding all
  77. # information required for looking up templates, i.e. view paths and
  78. # details. Check <tt>ActionView::LookupContext</tt> for more information.
  79. 1 def lookup_context
  80. 1 @_lookup_context ||=
  81. ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
  82. end
  83. 1 def details_for_lookup
  84. 1 {}
  85. end
  86. # Append a path to the list of view paths for the current <tt>LookupContext</tt>.
  87. #
  88. # ==== Parameters
  89. # * <tt>path</tt> - If a String is provided, it gets converted into
  90. # the default view path. You may also provide a custom view path
  91. # (see ActionView::PathSet for more information)
  92. 1 def append_view_path(path)
  93. lookup_context.view_paths.push(*path)
  94. end
  95. # Prepend a path to the list of view paths for the current <tt>LookupContext</tt>.
  96. #
  97. # ==== Parameters
  98. # * <tt>path</tt> - If a String is provided, it gets converted into
  99. # the default view path. You may also provide a custom view path
  100. # (see ActionView::PathSet for more information)
  101. 1 def prepend_view_path(path)
  102. lookup_context.view_paths.unshift(*path)
  103. end
  104. end
  105. end

vender/bundle/ruby/2.6.0/gems/activejob-6.1.3/lib/active_job/queue_adapters/test_adapter.rb

46.34% lines covered

41 relevant lines. 19 lines covered and 22 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveJob
  3. 1 module QueueAdapters
  4. # == Test adapter for Active Job
  5. #
  6. # The test adapter should be used only in testing. Along with
  7. # <tt>ActiveJob::TestCase</tt> and <tt>ActiveJob::TestHelper</tt>
  8. # it makes a great tool to test your Rails application.
  9. #
  10. # To use the test adapter set queue_adapter config to +:test+.
  11. #
  12. # Rails.application.config.active_job.queue_adapter = :test
  13. 1 class TestAdapter
  14. 1 attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue, :at)
  15. 1 attr_writer(:enqueued_jobs, :performed_jobs)
  16. # Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
  17. 1 def enqueued_jobs
  18. 3 @enqueued_jobs ||= []
  19. end
  20. # Provides a store of all the performed jobs with the TestAdapter so you can check them.
  21. 1 def performed_jobs
  22. 3 @performed_jobs ||= []
  23. end
  24. 1 def enqueue(job) #:nodoc:
  25. job_data = job_to_hash(job)
  26. perform_or_enqueue(perform_enqueued_jobs && !filtered?(job), job, job_data)
  27. end
  28. 1 def enqueue_at(job, timestamp) #:nodoc:
  29. job_data = job_to_hash(job, at: timestamp)
  30. perform_or_enqueue(perform_enqueued_at_jobs && !filtered?(job), job, job_data)
  31. end
  32. 1 private
  33. 1 def job_to_hash(job, extras = {})
  34. job.serialize.tap do |job_data|
  35. job_data[:job] = job.class
  36. job_data[:args] = job_data.fetch("arguments")
  37. job_data[:queue] = job_data.fetch("queue_name")
  38. end.merge(extras)
  39. end
  40. 1 def perform_or_enqueue(perform, job, job_data)
  41. if perform
  42. performed_jobs << job_data
  43. Base.execute(job.serialize)
  44. else
  45. enqueued_jobs << job_data
  46. end
  47. end
  48. 1 def filtered?(job)
  49. filtered_queue?(job) || filtered_job_class?(job) || filtered_time?(job)
  50. end
  51. 1 def filtered_time?(job)
  52. job.scheduled_at > at.to_f if at && job.scheduled_at
  53. end
  54. 1 def filtered_queue?(job)
  55. if queue
  56. job.queue_name != queue.to_s
  57. end
  58. end
  59. 1 def filtered_job_class?(job)
  60. if filter
  61. !filter_as_proc(filter).call(job)
  62. elsif reject
  63. filter_as_proc(reject).call(job)
  64. end
  65. end
  66. 1 def filter_as_proc(filter)
  67. return filter if filter.is_a?(Proc)
  68. ->(job) { Array(filter).include?(job.class) }
  69. end
  70. end
  71. end
  72. end

vender/bundle/ruby/2.6.0/gems/activejob-6.1.3/lib/active_job/test_helper.rb

34.15% lines covered

164 relevant lines. 56 lines covered and 108 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/class/subclasses"
  3. 1 require "active_support/testing/assertions"
  4. 1 module ActiveJob
  5. # Provides helper methods for testing Active Job
  6. 1 module TestHelper
  7. 1 delegate :enqueued_jobs, :enqueued_jobs=,
  8. :performed_jobs, :performed_jobs=,
  9. to: :queue_adapter
  10. 1 include ActiveSupport::Testing::Assertions
  11. 1 module TestQueueAdapter
  12. 1 extend ActiveSupport::Concern
  13. 1 included do
  14. 1 class_attribute :_test_adapter, instance_accessor: false, instance_predicate: false
  15. end
  16. 1 module ClassMethods
  17. 1 def queue_adapter
  18. 6 self._test_adapter.nil? ? super : self._test_adapter
  19. end
  20. 1 def disable_test_adapter
  21. 3 self._test_adapter = nil
  22. end
  23. 1 def enable_test_adapter(test_adapter)
  24. 3 self._test_adapter = test_adapter
  25. end
  26. end
  27. end
  28. 1 ActiveJob::Base.include(TestQueueAdapter)
  29. 1 def before_setup # :nodoc:
  30. 3 test_adapter = queue_adapter_for_test
  31. 3 queue_adapter_changed_jobs.each do |klass|
  32. 3 klass.enable_test_adapter(test_adapter)
  33. end
  34. 3 clear_enqueued_jobs
  35. 3 clear_performed_jobs
  36. 3 super
  37. end
  38. 1 def after_teardown # :nodoc:
  39. 3 super
  40. 6 queue_adapter_changed_jobs.each { |klass| klass.disable_test_adapter }
  41. end
  42. # Specifies the queue adapter to use with all Active Job test helpers.
  43. #
  44. # Returns an instance of the queue adapter and defaults to
  45. # <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
  46. #
  47. # Note: The adapter provided by this method must provide some additional
  48. # methods from those expected of a standard <tt>ActiveJob::QueueAdapter</tt>
  49. # in order to be used with the active job test helpers. Refer to
  50. # <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
  51. 1 def queue_adapter_for_test
  52. 3 ActiveJob::QueueAdapters::TestAdapter.new
  53. end
  54. # Asserts that the number of enqueued jobs matches the given number.
  55. #
  56. # def test_jobs
  57. # assert_enqueued_jobs 0
  58. # HelloJob.perform_later('david')
  59. # assert_enqueued_jobs 1
  60. # HelloJob.perform_later('abdelkader')
  61. # assert_enqueued_jobs 2
  62. # end
  63. #
  64. # If a block is passed, asserts that the block will cause the specified number of
  65. # jobs to be enqueued.
  66. #
  67. # def test_jobs_again
  68. # assert_enqueued_jobs 1 do
  69. # HelloJob.perform_later('cristian')
  70. # end
  71. #
  72. # assert_enqueued_jobs 2 do
  73. # HelloJob.perform_later('aaron')
  74. # HelloJob.perform_later('rafael')
  75. # end
  76. # end
  77. #
  78. # Asserts the number of times a specific job was enqueued by passing +:only+ option.
  79. #
  80. # def test_logging_job
  81. # assert_enqueued_jobs 1, only: LoggingJob do
  82. # LoggingJob.perform_later
  83. # HelloJob.perform_later('jeremy')
  84. # end
  85. # end
  86. #
  87. # Asserts the number of times a job except specific class was enqueued by passing +:except+ option.
  88. #
  89. # def test_logging_job
  90. # assert_enqueued_jobs 1, except: HelloJob do
  91. # LoggingJob.perform_later
  92. # HelloJob.perform_later('jeremy')
  93. # end
  94. # end
  95. #
  96. # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
  97. # a hash containing the job's class and it's argument are passed as argument.
  98. #
  99. # Asserts the number of times a job is enqueued to a specific queue by passing +:queue+ option.
  100. #
  101. # def test_logging_job
  102. # assert_enqueued_jobs 2, queue: 'default' do
  103. # LoggingJob.perform_later
  104. # HelloJob.perform_later('elfassy')
  105. # end
  106. # end
  107. 1 def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil, &block)
  108. if block_given?
  109. original_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
  110. assert_nothing_raised(&block)
  111. new_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
  112. actual_count = (new_jobs - original_jobs).count
  113. else
  114. actual_count = enqueued_jobs_with(only: only, except: except, queue: queue).count
  115. end
  116. assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
  117. end
  118. # Asserts that no jobs have been enqueued.
  119. #
  120. # def test_jobs
  121. # assert_no_enqueued_jobs
  122. # HelloJob.perform_later('jeremy')
  123. # assert_enqueued_jobs 1
  124. # end
  125. #
  126. # If a block is passed, asserts that the block will not cause any job to be enqueued.
  127. #
  128. # def test_jobs_again
  129. # assert_no_enqueued_jobs do
  130. # # No job should be enqueued from this block
  131. # end
  132. # end
  133. #
  134. # Asserts that no jobs of a specific kind are enqueued by passing +:only+ option.
  135. #
  136. # def test_no_logging
  137. # assert_no_enqueued_jobs only: LoggingJob do
  138. # HelloJob.perform_later('jeremy')
  139. # end
  140. # end
  141. #
  142. # Asserts that no jobs except specific class are enqueued by passing +:except+ option.
  143. #
  144. # def test_no_logging
  145. # assert_no_enqueued_jobs except: HelloJob do
  146. # HelloJob.perform_later('jeremy')
  147. # end
  148. # end
  149. #
  150. # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
  151. # a hash containing the job's class and it's argument are passed as argument.
  152. #
  153. # Asserts that no jobs are enqueued to a specific queue by passing +:queue+ option
  154. #
  155. # def test_no_logging
  156. # assert_no_enqueued_jobs queue: 'default' do
  157. # LoggingJob.set(queue: :some_queue).perform_later
  158. # end
  159. # end
  160. #
  161. # Note: This assertion is simply a shortcut for:
  162. #
  163. # assert_enqueued_jobs 0, &block
  164. 1 def assert_no_enqueued_jobs(only: nil, except: nil, queue: nil, &block)
  165. assert_enqueued_jobs 0, only: only, except: except, queue: queue, &block
  166. end
  167. # Asserts that the number of performed jobs matches the given number.
  168. # If no block is passed, <tt>perform_enqueued_jobs</tt>
  169. # must be called around or after the job call.
  170. #
  171. # def test_jobs
  172. # assert_performed_jobs 0
  173. #
  174. # perform_enqueued_jobs do
  175. # HelloJob.perform_later('xavier')
  176. # end
  177. # assert_performed_jobs 1
  178. #
  179. # HelloJob.perform_later('yves')
  180. #
  181. # perform_enqueued_jobs
  182. #
  183. # assert_performed_jobs 2
  184. # end
  185. #
  186. # If a block is passed, asserts that the block will cause the specified number of
  187. # jobs to be performed.
  188. #
  189. # def test_jobs_again
  190. # assert_performed_jobs 1 do
  191. # HelloJob.perform_later('robin')
  192. # end
  193. #
  194. # assert_performed_jobs 2 do
  195. # HelloJob.perform_later('carlos')
  196. # HelloJob.perform_later('sean')
  197. # end
  198. # end
  199. #
  200. # This method also supports filtering. If the +:only+ option is specified,
  201. # then only the listed job(s) will be performed.
  202. #
  203. # def test_hello_job
  204. # assert_performed_jobs 1, only: HelloJob do
  205. # HelloJob.perform_later('jeremy')
  206. # LoggingJob.perform_later
  207. # end
  208. # end
  209. #
  210. # Also if the +:except+ option is specified,
  211. # then the job(s) except specific class will be performed.
  212. #
  213. # def test_hello_job
  214. # assert_performed_jobs 1, except: LoggingJob do
  215. # HelloJob.perform_later('jeremy')
  216. # LoggingJob.perform_later
  217. # end
  218. # end
  219. #
  220. # An array may also be specified, to support testing multiple jobs.
  221. #
  222. # def test_hello_and_logging_jobs
  223. # assert_nothing_raised do
  224. # assert_performed_jobs 2, only: [HelloJob, LoggingJob] do
  225. # HelloJob.perform_later('jeremy')
  226. # LoggingJob.perform_later('stewie')
  227. # RescueJob.perform_later('david')
  228. # end
  229. # end
  230. # end
  231. #
  232. # A proc may also be specified. When passed a Proc, the job's instance will be passed as argument.
  233. #
  234. # def test_hello_and_logging_jobs
  235. # assert_nothing_raised do
  236. # assert_performed_jobs(1, only: ->(job) { job.is_a?(HelloJob) }) do
  237. # HelloJob.perform_later('jeremy')
  238. # LoggingJob.perform_later('stewie')
  239. # RescueJob.perform_later('david')
  240. # end
  241. # end
  242. # end
  243. #
  244. # If the +:queue+ option is specified,
  245. # then only the job(s) enqueued to a specific queue will be performed.
  246. #
  247. # def test_assert_performed_jobs_with_queue_option
  248. # assert_performed_jobs 1, queue: :some_queue do
  249. # HelloJob.set(queue: :some_queue).perform_later("jeremy")
  250. # HelloJob.set(queue: :other_queue).perform_later("bogdan")
  251. # end
  252. # end
  253. 1 def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
  254. if block_given?
  255. original_count = performed_jobs.size
  256. perform_enqueued_jobs(only: only, except: except, queue: queue, &block)
  257. new_count = performed_jobs.size
  258. performed_jobs_size = new_count - original_count
  259. else
  260. performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue).count
  261. end
  262. assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
  263. end
  264. # Asserts that no jobs have been performed.
  265. #
  266. # def test_jobs
  267. # assert_no_performed_jobs
  268. #
  269. # perform_enqueued_jobs do
  270. # HelloJob.perform_later('matthew')
  271. # assert_performed_jobs 1
  272. # end
  273. # end
  274. #
  275. # If a block is passed, asserts that the block will not cause any job to be performed.
  276. #
  277. # def test_jobs_again
  278. # assert_no_performed_jobs do
  279. # # No job should be performed from this block
  280. # end
  281. # end
  282. #
  283. # The block form supports filtering. If the +:only+ option is specified,
  284. # then only the listed job(s) will not be performed.
  285. #
  286. # def test_no_logging
  287. # assert_no_performed_jobs only: LoggingJob do
  288. # HelloJob.perform_later('jeremy')
  289. # end
  290. # end
  291. #
  292. # Also if the +:except+ option is specified,
  293. # then the job(s) except specific class will not be performed.
  294. #
  295. # def test_no_logging
  296. # assert_no_performed_jobs except: HelloJob do
  297. # HelloJob.perform_later('jeremy')
  298. # end
  299. # end
  300. #
  301. # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
  302. # an instance of the job will be passed as argument.
  303. #
  304. # If the +:queue+ option is specified,
  305. # then only the job(s) enqueued to a specific queue will not be performed.
  306. #
  307. # def test_assert_no_performed_jobs_with_queue_option
  308. # assert_no_performed_jobs queue: :some_queue do
  309. # HelloJob.set(queue: :other_queue).perform_later("jeremy")
  310. # end
  311. # end
  312. #
  313. # Note: This assertion is simply a shortcut for:
  314. #
  315. # assert_performed_jobs 0, &block
  316. 1 def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
  317. assert_performed_jobs 0, only: only, except: except, queue: queue, &block
  318. end
  319. # Asserts that the job has been enqueued with the given arguments.
  320. #
  321. # def test_assert_enqueued_with
  322. # MyJob.perform_later(1,2,3)
  323. # assert_enqueued_with(job: MyJob, args: [1,2,3])
  324. #
  325. # MyJob.set(wait_until: Date.tomorrow.noon, queue: "my_queue").perform_later
  326. # assert_enqueued_with(at: Date.tomorrow.noon, queue: "my_queue")
  327. # end
  328. #
  329. # The given arguments may also be specified as matcher procs that return a
  330. # boolean value indicating whether a job's attribute meets certain criteria.
  331. #
  332. # For example, a proc can be used to match a range of times:
  333. #
  334. # def test_assert_enqueued_with
  335. # at_matcher = ->(job_at) { (Date.yesterday..Date.tomorrow).cover?(job_at) }
  336. #
  337. # MyJob.set(wait_until: Date.today.noon).perform_later
  338. #
  339. # assert_enqueued_with(job: MyJob, at: at_matcher)
  340. # end
  341. #
  342. # A proc can also be used to match a subset of a job's args:
  343. #
  344. # def test_assert_enqueued_with
  345. # args_matcher = ->(job_args) { job_args[0].key?(:foo) }
  346. #
  347. # MyJob.perform_later(foo: "bar", other_arg: "No need to check in the test")
  348. #
  349. # assert_enqueued_with(job: MyJob, args: args_matcher)
  350. # end
  351. #
  352. # If a block is passed, asserts that the block will cause the job to be
  353. # enqueued with the given arguments.
  354. #
  355. # def test_assert_enqueued_with
  356. # assert_enqueued_with(job: MyJob, args: [1,2,3]) do
  357. # MyJob.perform_later(1,2,3)
  358. # end
  359. #
  360. # assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon) do
  361. # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
  362. # end
  363. # end
  364. 1 def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil, &block)
  365. expected = { job: job, args: args, at: at, queue: queue }.compact
  366. expected_args = prepare_args_for_assertion(expected)
  367. potential_matches = []
  368. if block_given?
  369. original_enqueued_jobs = enqueued_jobs.dup
  370. assert_nothing_raised(&block)
  371. jobs = enqueued_jobs - original_enqueued_jobs
  372. else
  373. jobs = enqueued_jobs
  374. end
  375. matching_job = jobs.find do |enqueued_job|
  376. deserialized_job = deserialize_args_for_assertion(enqueued_job)
  377. potential_matches << deserialized_job
  378. expected_args.all? do |key, value|
  379. if value.respond_to?(:call)
  380. value.call(deserialized_job[key])
  381. else
  382. value == deserialized_job[key]
  383. end
  384. end
  385. end
  386. message = +"No enqueued job found with #{expected}"
  387. message << "\n\nPotential matches: #{potential_matches.join("\n")}" if potential_matches.present?
  388. assert matching_job, message
  389. instantiate_job(matching_job)
  390. end
  391. # Asserts that the job has been performed with the given arguments.
  392. #
  393. # def test_assert_performed_with
  394. # MyJob.perform_later(1,2,3)
  395. #
  396. # perform_enqueued_jobs
  397. #
  398. # assert_performed_with(job: MyJob, args: [1,2,3])
  399. #
  400. # MyJob.set(wait_until: Date.tomorrow.noon, queue: "my_queue").perform_later
  401. #
  402. # perform_enqueued_jobs
  403. #
  404. # assert_performed_with(at: Date.tomorrow.noon, queue: "my_queue")
  405. # end
  406. #
  407. # The given arguments may also be specified as matcher procs that return a
  408. # boolean value indicating whether a job's attribute meets certain criteria.
  409. #
  410. # For example, a proc can be used to match a range of times:
  411. #
  412. # def test_assert_performed_with
  413. # at_matcher = ->(job_at) { (Date.yesterday..Date.tomorrow).cover?(job_at) }
  414. #
  415. # MyJob.set(wait_until: Date.today.noon).perform_later
  416. #
  417. # perform_enqueued_jobs
  418. #
  419. # assert_performed_with(job: MyJob, at: at_matcher)
  420. # end
  421. #
  422. # A proc can also be used to match a subset of a job's args:
  423. #
  424. # def test_assert_performed_with
  425. # args_matcher = ->(job_args) { job_args[0].key?(:foo) }
  426. #
  427. # MyJob.perform_later(foo: "bar", other_arg: "No need to check in the test")
  428. #
  429. # perform_enqueued_jobs
  430. #
  431. # assert_performed_with(job: MyJob, args: args_matcher)
  432. # end
  433. #
  434. # If a block is passed, that block performs all of the jobs that were
  435. # enqueued throughout the duration of the block and asserts that
  436. # the job has been performed with the given arguments in the block.
  437. #
  438. # def test_assert_performed_with
  439. # assert_performed_with(job: MyJob, args: [1,2,3]) do
  440. # MyJob.perform_later(1,2,3)
  441. # end
  442. #
  443. # assert_performed_with(job: MyJob, at: Date.tomorrow.noon) do
  444. # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
  445. # end
  446. # end
  447. 1 def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, &block)
  448. expected = { job: job, args: args, at: at, queue: queue }.compact
  449. expected_args = prepare_args_for_assertion(expected)
  450. potential_matches = []
  451. if block_given?
  452. original_performed_jobs_count = performed_jobs.count
  453. perform_enqueued_jobs(&block)
  454. jobs = performed_jobs.drop(original_performed_jobs_count)
  455. else
  456. jobs = performed_jobs
  457. end
  458. matching_job = jobs.find do |enqueued_job|
  459. deserialized_job = deserialize_args_for_assertion(enqueued_job)
  460. potential_matches << deserialized_job
  461. expected_args.all? do |key, value|
  462. if value.respond_to?(:call)
  463. value.call(deserialized_job[key])
  464. else
  465. value == deserialized_job[key]
  466. end
  467. end
  468. end
  469. message = +"No performed job found with #{expected}"
  470. message << "\n\nPotential matches: #{potential_matches.join("\n")}" if potential_matches.present?
  471. assert matching_job, message
  472. instantiate_job(matching_job)
  473. end
  474. # Performs all enqueued jobs. If a block is given, performs all of the jobs
  475. # that were enqueued throughout the duration of the block. If a block is
  476. # not given, performs all of the enqueued jobs up to this point in the test.
  477. #
  478. # def test_perform_enqueued_jobs
  479. # perform_enqueued_jobs do
  480. # MyJob.perform_later(1, 2, 3)
  481. # end
  482. # assert_performed_jobs 1
  483. # end
  484. #
  485. # def test_perform_enqueued_jobs_without_block
  486. # MyJob.perform_later(1, 2, 3)
  487. #
  488. # perform_enqueued_jobs
  489. #
  490. # assert_performed_jobs 1
  491. # end
  492. #
  493. # This method also supports filtering. If the +:only+ option is specified,
  494. # then only the listed job(s) will be performed.
  495. #
  496. # def test_perform_enqueued_jobs_with_only
  497. # perform_enqueued_jobs(only: MyJob) do
  498. # MyJob.perform_later(1, 2, 3) # will be performed
  499. # HelloJob.perform_later(1, 2, 3) # will not be performed
  500. # end
  501. # assert_performed_jobs 1
  502. # end
  503. #
  504. # Also if the +:except+ option is specified,
  505. # then the job(s) except specific class will be performed.
  506. #
  507. # def test_perform_enqueued_jobs_with_except
  508. # perform_enqueued_jobs(except: HelloJob) do
  509. # MyJob.perform_later(1, 2, 3) # will be performed
  510. # HelloJob.perform_later(1, 2, 3) # will not be performed
  511. # end
  512. # assert_performed_jobs 1
  513. # end
  514. #
  515. # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
  516. # an instance of the job will be passed as argument.
  517. #
  518. # If the +:queue+ option is specified,
  519. # then only the job(s) enqueued to a specific queue will be performed.
  520. #
  521. # def test_perform_enqueued_jobs_with_queue
  522. # perform_enqueued_jobs queue: :some_queue do
  523. # MyJob.set(queue: :some_queue).perform_later(1, 2, 3) # will be performed
  524. # HelloJob.set(queue: :other_queue).perform_later(1, 2, 3) # will not be performed
  525. # end
  526. # assert_performed_jobs 1
  527. # end
  528. #
  529. # If the +:at+ option is specified, then only run jobs enqueued to run
  530. # immediately or before the given time
  531. 1 def perform_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil, &block)
  532. return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at) unless block_given?
  533. validate_option(only: only, except: except)
  534. old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
  535. old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
  536. old_filter = queue_adapter.filter
  537. old_reject = queue_adapter.reject
  538. old_queue = queue_adapter.queue
  539. old_at = queue_adapter.at
  540. begin
  541. queue_adapter.perform_enqueued_jobs = true
  542. queue_adapter.perform_enqueued_at_jobs = true
  543. queue_adapter.filter = only
  544. queue_adapter.reject = except
  545. queue_adapter.queue = queue
  546. queue_adapter.at = at
  547. assert_nothing_raised(&block)
  548. ensure
  549. queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
  550. queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
  551. queue_adapter.filter = old_filter
  552. queue_adapter.reject = old_reject
  553. queue_adapter.queue = old_queue
  554. queue_adapter.at = old_at
  555. end
  556. end
  557. # Accesses the queue_adapter set by ActiveJob::Base.
  558. #
  559. # def test_assert_job_has_custom_queue_adapter_set
  560. # assert_instance_of CustomQueueAdapter, HelloJob.queue_adapter
  561. # end
  562. 1 def queue_adapter
  563. 6 ActiveJob::Base.queue_adapter
  564. end
  565. 1 private
  566. 1 def clear_enqueued_jobs
  567. 3 enqueued_jobs.clear
  568. end
  569. 1 def clear_performed_jobs
  570. 3 performed_jobs.clear
  571. end
  572. 1 def jobs_with(jobs, only: nil, except: nil, queue: nil, at: nil)
  573. validate_option(only: only, except: except)
  574. jobs.dup.select do |job|
  575. job_class = job.fetch(:job)
  576. if only
  577. next false unless filter_as_proc(only).call(job)
  578. elsif except
  579. next false if filter_as_proc(except).call(job)
  580. end
  581. if queue
  582. next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
  583. end
  584. if at && job[:at]
  585. next false if job[:at] > at.to_f
  586. end
  587. yield job if block_given?
  588. true
  589. end
  590. end
  591. 1 def filter_as_proc(filter)
  592. return filter if filter.is_a?(Proc)
  593. ->(job) { Array(filter).include?(job.fetch(:job)) }
  594. end
  595. 1 def enqueued_jobs_with(only: nil, except: nil, queue: nil, at: nil, &block)
  596. jobs_with(enqueued_jobs, only: only, except: except, queue: queue, at: at, &block)
  597. end
  598. 1 def performed_jobs_with(only: nil, except: nil, queue: nil, &block)
  599. jobs_with(performed_jobs, only: only, except: except, queue: queue, &block)
  600. end
  601. 1 def flush_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil)
  602. enqueued_jobs_with(only: only, except: except, queue: queue, at: at) do |payload|
  603. queue_adapter.enqueued_jobs.delete(payload)
  604. queue_adapter.performed_jobs << payload
  605. instantiate_job(payload).perform_now
  606. end.count
  607. end
  608. 1 def prepare_args_for_assertion(args)
  609. args.dup.tap do |arguments|
  610. if arguments[:at].acts_like?(:time)
  611. at_range = arguments[:at] - 1..arguments[:at] + 1
  612. arguments[:at] = ->(at) { at_range.cover?(at) }
  613. end
  614. end
  615. end
  616. 1 def deserialize_args_for_assertion(job)
  617. job.dup.tap do |new_job|
  618. new_job[:at] = Time.at(new_job[:at]) if new_job[:at]
  619. new_job[:args] = ActiveJob::Arguments.deserialize(new_job[:args]) if new_job[:args]
  620. end
  621. end
  622. 1 def instantiate_job(payload)
  623. job = payload[:job].deserialize(payload)
  624. job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at)
  625. job.send(:deserialize_arguments_if_needed)
  626. job
  627. end
  628. 1 def queue_adapter_changed_jobs
  629. 6 (ActiveJob::Base.descendants << ActiveJob::Base).select do |klass|
  630. # only override explicitly set adapters, a quirk of `class_attribute`
  631. 30 klass.singleton_class.public_instance_methods(false).include?(:_queue_adapter)
  632. end
  633. end
  634. 1 def validate_option(only: nil, except: nil)
  635. raise ArgumentError, "Cannot specify both `:only` and `:except` options." if only && except
  636. end
  637. end
  638. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/alias_tracker.rb

52.38% lines covered

42 relevant lines. 22 lines covered and 20 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/string/conversions"
  3. 1 module ActiveRecord
  4. 1 module Associations
  5. # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
  6. 1 class AliasTracker # :nodoc:
  7. 1 def self.create(connection, initial_table, joins, aliases = nil)
  8. 11 if joins.empty?
  9. 11 aliases ||= Hash.new(0)
  10. elsif aliases
  11. default_proc = aliases.default_proc || proc { 0 }
  12. aliases.default_proc = proc { |h, k|
  13. h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
  14. }
  15. else
  16. aliases = Hash.new { |h, k|
  17. h[k] = initial_count_for(connection, k, joins)
  18. }
  19. end
  20. 11 aliases[initial_table] = 1
  21. 11 new(connection, aliases)
  22. end
  23. 1 def self.initial_count_for(connection, name, table_joins)
  24. quoted_name = nil
  25. counts = table_joins.map do |join|
  26. if join.is_a?(Arel::Nodes::StringJoin)
  27. # quoted_name should be case ignored as some database adapters (Oracle) return quoted name in uppercase
  28. quoted_name ||= connection.quote_table_name(name)
  29. # Table names + table aliases
  30. join.left.scan(
  31. /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
  32. ).size
  33. elsif join.is_a?(Arel::Nodes::Join)
  34. join.left.name == name ? 1 : 0
  35. else
  36. raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
  37. end
  38. end
  39. counts.sum
  40. end
  41. # table_joins is an array of arel joins which might conflict with the aliases we assign here
  42. 1 def initialize(connection, aliases)
  43. 11 @aliases = aliases
  44. 11 @connection = connection
  45. end
  46. 1 def aliased_table_for(arel_table, table_name = nil)
  47. 1 table_name ||= arel_table.name
  48. 1 if aliases[table_name] == 0
  49. # If it's zero, we can have our table_name
  50. 1 aliases[table_name] = 1
  51. 1 arel_table = arel_table.alias(table_name) if arel_table.name != table_name
  52. else
  53. # Otherwise, we need to use an alias
  54. aliased_name = @connection.table_alias_for(yield)
  55. # Update the count
  56. count = aliases[aliased_name] += 1
  57. aliased_name = "#{truncate(aliased_name)}_#{count}" if count > 1
  58. arel_table = arel_table.alias(aliased_name)
  59. end
  60. 1 arel_table
  61. end
  62. 1 attr_reader :aliases
  63. 1 private
  64. 1 def truncate(name)
  65. name.slice(0, @connection.table_alias_length - 2)
  66. end
  67. end
  68. end
  69. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/association.rb

76.03% lines covered

146 relevant lines. 111 lines covered and 35 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module Associations
  4. # = Active Record Associations
  5. #
  6. # This is the root class of all associations ('+ Foo' signifies an included module Foo):
  7. #
  8. # Association
  9. # SingularAssociation
  10. # HasOneAssociation + ForeignAssociation
  11. # HasOneThroughAssociation + ThroughAssociation
  12. # BelongsToAssociation
  13. # BelongsToPolymorphicAssociation
  14. # CollectionAssociation
  15. # HasManyAssociation + ForeignAssociation
  16. # HasManyThroughAssociation + ThroughAssociation
  17. #
  18. # Associations in Active Record are middlemen between the object that
  19. # holds the association, known as the <tt>owner</tt>, and the associated
  20. # result set, known as the <tt>target</tt>. Association metadata is available in
  21. # <tt>reflection</tt>, which is an instance of <tt>ActiveRecord::Reflection::AssociationReflection</tt>.
  22. #
  23. # For example, given
  24. #
  25. # class Blog < ActiveRecord::Base
  26. # has_many :posts
  27. # end
  28. #
  29. # blog = Blog.first
  30. #
  31. # The association of <tt>blog.posts</tt> has the object +blog+ as its
  32. # <tt>owner</tt>, the collection of its posts as <tt>target</tt>, and
  33. # the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
  34. 1 class Association #:nodoc:
  35. 1 attr_reader :owner, :target, :reflection
  36. 1 delegate :options, to: :reflection
  37. 1 def initialize(owner, reflection)
  38. 52 reflection.check_validity!
  39. 52 @owner, @reflection = owner, reflection
  40. 52 reset
  41. 52 reset_scope
  42. end
  43. # Resets the \loaded flag to +false+ and sets the \target to +nil+.
  44. 1 def reset
  45. 58 @loaded = false
  46. 58 @target = nil
  47. 58 @stale_state = nil
  48. 58 @inversed = false
  49. end
  50. 1 def reset_negative_cache # :nodoc:
  51. reset if loaded? && target.nil?
  52. end
  53. # Reloads the \target and returns +self+ on success.
  54. # The QueryCache is cleared if +force+ is true.
  55. 1 def reload(force = false)
  56. 6 klass.connection.clear_query_cache if force && klass
  57. 6 reset
  58. 6 reset_scope
  59. 6 load_target
  60. 6 self unless target.nil?
  61. end
  62. # Has the \target been already \loaded?
  63. 1 def loaded?
  64. 394 @loaded
  65. end
  66. # Asserts the \target has been loaded setting the \loaded flag to +true+.
  67. 1 def loaded!
  68. 95 @loaded = true
  69. 95 @stale_state = stale_state
  70. 95 @inversed = false
  71. end
  72. # The target is stale if the target no longer points to the record(s) that the
  73. # relevant foreign_key(s) refers to. If stale, the association accessor method
  74. # on the owner will reload the target. It's up to subclasses to implement the
  75. # stale_state method if relevant.
  76. #
  77. # Note that if the target has not been loaded, it is not considered stale.
  78. 1 def stale_target?
  79. 168 !@inversed && loaded? && @stale_state != stale_state
  80. end
  81. # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
  82. 1 def target=(target)
  83. 48 @target = target
  84. 48 loaded!
  85. end
  86. 1 def scope
  87. 14 if (scope = klass.current_scope) && scope.try(:proxy_association) == self
  88. scope.spawn
  89. else
  90. 14 target_scope.merge!(association_scope)
  91. end
  92. end
  93. 1 def reset_scope
  94. 58 @association_scope = nil
  95. end
  96. # Set the inverse association, if possible
  97. 1 def set_inverse_instance(record)
  98. 56 if inverse = inverse_association_for(record)
  99. 6 inverse.inversed_from(owner)
  100. end
  101. 56 record
  102. end
  103. 1 def set_inverse_instance_from_queries(record)
  104. if inverse = inverse_association_for(record)
  105. inverse.inversed_from_queries(owner)
  106. end
  107. record
  108. end
  109. # Remove the inverse association, if possible
  110. 1 def remove_inverse_instance(record)
  111. if inverse = inverse_association_for(record)
  112. inverse.inversed_from(nil)
  113. end
  114. end
  115. 1 def inversed_from(record)
  116. 6 self.target = record
  117. 6 @inversed = !!record
  118. end
  119. 1 def inversed_from_queries(record)
  120. if inversable?(record)
  121. self.target = record
  122. @inversed = true
  123. else
  124. @inversed = false
  125. end
  126. end
  127. # Returns the class of the target. belongs_to polymorphic overrides this to look at the
  128. # polymorphic_type field on the owner.
  129. 1 def klass
  130. 126 reflection.klass
  131. end
  132. 1 def extensions
  133. 3 extensions = klass.default_extensions | reflection.extensions
  134. 3 if reflection.scope
  135. extensions |= reflection.scope_for(klass.unscoped, owner).extensions
  136. end
  137. 3 extensions
  138. end
  139. # Loads the \target if needed and returns it.
  140. #
  141. # This method is abstract in the sense that it relies on +find_target+,
  142. # which is expected to be provided by descendants.
  143. #
  144. # If the \target is already \loaded it is just returned. Thus, you can call
  145. # +load_target+ unconditionally to get the \target.
  146. #
  147. # ActiveRecord::RecordNotFound is rescued within the method, and it is
  148. # not reraised. The proxy is \reset and +nil+ is the return value.
  149. 1 def load_target
  150. 56 @target = find_target if (@stale_state && stale_target?) || find_target?
  151. 56 loaded! unless loaded?
  152. 56 target
  153. rescue ActiveRecord::RecordNotFound
  154. reset
  155. end
  156. # We can't dump @reflection and @through_reflection since it contains the scope proc
  157. 1 def marshal_dump
  158. ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
  159. [@reflection.name, ivars]
  160. end
  161. 1 def marshal_load(data)
  162. reflection_name, ivars = data
  163. ivars.each { |name, val| instance_variable_set(name, val) }
  164. @reflection = @owner.class._reflect_on_association(reflection_name)
  165. end
  166. 1 def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
  167. 4 except_from_scope_attributes ||= {}
  168. 4 skip_assign = [reflection.foreign_key, reflection.type].compact
  169. 4 assigned_keys = record.changed_attribute_names_to_save
  170. 4 assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
  171. 4 attributes = scope_for_create.except!(*(assigned_keys - skip_assign))
  172. 4 record.send(:_assign_attributes, attributes) if attributes.any?
  173. 4 set_inverse_instance(record)
  174. end
  175. 1 def create(attributes = nil, &block)
  176. 3 _create_record(attributes, &block)
  177. end
  178. 1 def create!(attributes = nil, &block)
  179. _create_record(attributes, true, &block)
  180. end
  181. 1 private
  182. 1 def find_target
  183. 6 if (owner.strict_loading? || reflection.strict_loading?) && owner.validation_context.nil?
  184. Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
  185. end
  186. 6 scope = self.scope
  187. 6 return scope.to_a if skip_statement_cache?(scope)
  188. 6 sc = reflection.association_scope_cache(klass, owner) do |params|
  189. 4 as = AssociationScope.create { params.bind }
  190. 2 target_scope.merge!(as.scope(self))
  191. end
  192. 6 binds = AssociationScope.get_bind_values(owner, reflection.chain)
  193. 12 sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) }
  194. end
  195. # The scope for this association.
  196. #
  197. # Note that the association_scope is merged into the target_scope only when the
  198. # scope method is called. This is because at that point the call may be surrounded
  199. # by scope.scoping { ... } or unscoped { ... } etc, which affects the scope which
  200. # actually gets built.
  201. 1 def association_scope
  202. 17 if klass
  203. 17 @association_scope ||= AssociationScope.scope(self)
  204. end
  205. end
  206. # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
  207. # through association's scope)
  208. 1 def target_scope
  209. 16 AssociationRelation.create(klass, self).merge!(klass.scope_for_association)
  210. end
  211. 1 def scope_for_create
  212. 4 scope.scope_for_create
  213. end
  214. 1 def find_target?
  215. !loaded? && (!owner.new_record? || foreign_key_present?) && klass
  216. end
  217. # Returns true if there is a foreign key present on the owner which
  218. # references the target. This is used to determine whether we can load
  219. # the target if the owner is currently a new record (and therefore
  220. # without a key). If the owner is a new record then foreign_key must
  221. # be present in order to load target.
  222. #
  223. # Currently implemented by belongs_to (vanilla and polymorphic) and
  224. # has_one/has_many :through associations which go through a belongs_to.
  225. 1 def foreign_key_present?
  226. false
  227. end
  228. # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
  229. # the kind of the class of the associated objects. Meant to be used as
  230. # a sanity check when you are about to assign an associated record.
  231. 1 def raise_on_type_mismatch!(record)
  232. 42 unless record.is_a?(reflection.klass)
  233. fresh_class = reflection.class_name.safe_constantize
  234. unless fresh_class && record.is_a?(fresh_class)
  235. message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, "\
  236. "got #{record.inspect} which is an instance of #{record.class}(##{record.class.object_id})"
  237. raise ActiveRecord::AssociationTypeMismatch, message
  238. end
  239. end
  240. end
  241. 1 def inverse_association_for(record)
  242. 56 if invertible_for?(record)
  243. 6 record.association(inverse_reflection_for(record).name)
  244. end
  245. end
  246. # Can be redefined by subclasses, notably polymorphic belongs_to
  247. # The record parameter is necessary to support polymorphic inverses as we must check for
  248. # the association in the specific class of the record.
  249. 1 def inverse_reflection_for(record)
  250. 60 reflection.inverse_of
  251. end
  252. # Returns true if inverse association on the given record needs to be set.
  253. # This method is redefined by subclasses.
  254. 1 def invertible_for?(record)
  255. 6 foreign_key_for?(record) && inverse_reflection_for(record)
  256. end
  257. # Returns true if record contains the foreign_key
  258. 1 def foreign_key_for?(record)
  259. 6 record._has_attribute?(reflection.foreign_key)
  260. end
  261. # This should be implemented to return the values of the relevant key(s) on the owner,
  262. # so that when stale_state is different from the value stored on the last find_target,
  263. # the target is stale.
  264. #
  265. # This is only relevant to certain associations, which is why it returns +nil+ by default.
  266. 1 def stale_state
  267. end
  268. 1 def build_record(attributes)
  269. 4 reflection.build_association(attributes) do |record|
  270. 4 initialize_attributes(record, attributes)
  271. 4 yield(record) if block_given?
  272. end
  273. end
  274. # Returns true if statement cache should be skipped on the association reader.
  275. 1 def skip_statement_cache?(scope)
  276. 6 reflection.has_scope? ||
  277. scope.eager_loading? ||
  278. klass.scope_attributes? ||
  279. reflection.source_reflection.active_record.default_scopes.any?
  280. end
  281. 1 def enqueue_destroy_association(options)
  282. job_class = owner.class.destroy_association_async_job
  283. if job_class
  284. owner._after_commit_jobs.push([job_class, options])
  285. end
  286. end
  287. 1 def inversable?(record)
  288. record &&
  289. ((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
  290. end
  291. 1 def matches_foreign_key?(record)
  292. if foreign_key_for?(record)
  293. record.read_attribute(reflection.foreign_key) == owner.id ||
  294. (foreign_key_for?(owner) && owner.read_attribute(reflection.foreign_key) == record.id)
  295. else
  296. owner.read_attribute(reflection.foreign_key) == record.id
  297. end
  298. end
  299. end
  300. end
  301. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/association_scope.rb

76.77% lines covered

99 relevant lines. 76 lines covered and 23 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module Associations
  4. 1 class AssociationScope #:nodoc:
  5. 1 def self.scope(association)
  6. 9 INSTANCE.scope(association)
  7. end
  8. 1 def self.create(&block)
  9. 12 block ||= lambda { |val| val }
  10. 3 new(block)
  11. end
  12. 1 def initialize(value_transformation)
  13. 3 @value_transformation = value_transformation
  14. end
  15. 1 INSTANCE = create
  16. 1 def scope(association)
  17. 11 klass = association.klass
  18. 11 reflection = association.reflection
  19. 11 scope = klass.unscoped
  20. 11 owner = association.owner
  21. 11 chain = get_chain(reflection, association, scope.alias_tracker)
  22. 11 scope.extending! reflection.extensions
  23. 11 scope = add_constraints(scope, owner, chain)
  24. 11 scope.limit!(1) unless reflection.collection?
  25. 11 scope
  26. end
  27. 1 def self.get_bind_values(owner, chain)
  28. 6 binds = []
  29. 6 last_reflection = chain.last
  30. 6 binds << last_reflection.join_id_for(owner)
  31. 6 if last_reflection.type
  32. binds << owner.class.polymorphic_name
  33. end
  34. 6 chain.each_cons(2).each do |reflection, next_reflection|
  35. if reflection.type
  36. binds << next_reflection.klass.polymorphic_name
  37. end
  38. end
  39. 6 binds
  40. end
  41. 1 private
  42. 1 attr_reader :value_transformation
  43. 1 def join(table, constraint)
  44. 1 Arel::Nodes::LeadingJoin.new(table, Arel::Nodes::On.new(constraint))
  45. end
  46. 1 def last_chain_scope(scope, reflection, owner)
  47. 11 primary_key = reflection.join_primary_key
  48. 11 foreign_key = reflection.join_foreign_key
  49. 11 table = reflection.aliased_table
  50. 11 value = transform_value(owner[foreign_key])
  51. 11 scope = apply_scope(scope, table, primary_key, value)
  52. 11 if reflection.type
  53. polymorphic_type = transform_value(owner.class.polymorphic_name)
  54. scope = apply_scope(scope, table, reflection.type, polymorphic_type)
  55. end
  56. 11 scope
  57. end
  58. 1 def transform_value(value)
  59. 11 value_transformation.call(value)
  60. end
  61. 1 def next_chain_scope(scope, reflection, next_reflection)
  62. 1 primary_key = reflection.join_primary_key
  63. 1 foreign_key = reflection.join_foreign_key
  64. 1 table = reflection.aliased_table
  65. 1 foreign_table = next_reflection.aliased_table
  66. 1 constraint = table[primary_key].eq(foreign_table[foreign_key])
  67. 1 if reflection.type
  68. value = transform_value(next_reflection.klass.polymorphic_name)
  69. scope = apply_scope(scope, table, reflection.type, value)
  70. end
  71. 1 scope.joins!(join(foreign_table, constraint))
  72. end
  73. 1 class ReflectionProxy < SimpleDelegator # :nodoc:
  74. 1 attr_reader :aliased_table
  75. 1 def initialize(reflection, aliased_table)
  76. 1 super(reflection)
  77. 1 @aliased_table = aliased_table
  78. end
  79. 1 def all_includes; nil; end
  80. end
  81. 1 def get_chain(reflection, association, tracker)
  82. 11 name = reflection.name
  83. 11 chain = [Reflection::RuntimeReflection.new(reflection, association)]
  84. 11 reflection.chain.drop(1).each do |refl|
  85. 1 aliased_table = tracker.aliased_table_for(refl.klass.arel_table) do
  86. refl.alias_candidate(name)
  87. end
  88. 1 chain << ReflectionProxy.new(refl, aliased_table)
  89. end
  90. 11 chain
  91. end
  92. 1 def add_constraints(scope, owner, chain)
  93. 11 scope = last_chain_scope(scope, chain.last, owner)
  94. 11 chain.each_cons(2) do |reflection, next_reflection|
  95. 1 scope = next_chain_scope(scope, reflection, next_reflection)
  96. end
  97. 11 chain_head = chain.first
  98. 11 chain.reverse_each do |reflection|
  99. # Exclude the scope of the association itself, because that
  100. # was already merged in the #scope method.
  101. 12 reflection.constraints.each do |scope_chain_item|
  102. item = eval_scope(reflection, scope_chain_item, owner)
  103. if scope_chain_item == chain_head.scope
  104. scope.merge! item.except(:where, :includes, :unscope, :order)
  105. elsif !item.references_values.empty?
  106. scope.merge! item.only(:joins, :left_outer_joins)
  107. associations = item.eager_load_values | item.includes_values
  108. unless associations.empty?
  109. scope.joins! item.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
  110. end
  111. end
  112. reflection.all_includes do
  113. scope.includes_values |= item.includes_values
  114. end
  115. scope.unscope!(*item.unscope_values)
  116. scope.where_clause += item.where_clause
  117. scope.order_values = item.order_values | scope.order_values
  118. end
  119. end
  120. 11 scope
  121. end
  122. 1 def apply_scope(scope, table, key, value)
  123. 11 if scope.table == table
  124. 10 scope.where!(key => value)
  125. else
  126. 1 scope.where!(table.name => { key => value })
  127. end
  128. end
  129. 1 def eval_scope(reflection, scope, owner)
  130. relation = reflection.build_scope(reflection.aliased_table)
  131. relation.instance_exec(owner, &scope) || relation
  132. end
  133. end
  134. end
  135. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/belongs_to_association.rb

63.77% lines covered

69 relevant lines. 44 lines covered and 25 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module Associations
  4. # = Active Record Belongs To Association
  5. 1 class BelongsToAssociation < SingularAssociation #:nodoc:
  6. 1 def handle_dependency
  7. return unless load_target
  8. case options[:dependent]
  9. when :destroy
  10. target.destroy
  11. raise ActiveRecord::Rollback unless target.destroyed?
  12. when :destroy_async
  13. id = owner.public_send(reflection.foreign_key.to_sym)
  14. primary_key_column = reflection.active_record_primary_key.to_sym
  15. enqueue_destroy_association(
  16. owner_model_name: owner.class.to_s,
  17. owner_id: owner.id,
  18. association_class: reflection.klass.to_s,
  19. association_ids: [id],
  20. association_primary_key_column: primary_key_column,
  21. ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
  22. )
  23. else
  24. target.public_send(options[:dependent])
  25. end
  26. end
  27. 1 def inversed_from(record)
  28. 6 replace_keys(record)
  29. 6 super
  30. end
  31. 1 def default(&block)
  32. writer(owner.instance_exec(&block)) if reader.nil?
  33. end
  34. 1 def reset
  35. 54 super
  36. 54 @updated = false
  37. end
  38. 1 def updated?
  39. 50 @updated
  40. end
  41. 1 def decrement_counters
  42. update_counters(-1)
  43. end
  44. 1 def increment_counters
  45. update_counters(1)
  46. end
  47. 1 def decrement_counters_before_last_save
  48. if reflection.polymorphic?
  49. model_was = owner.attribute_before_last_save(reflection.foreign_type)&.constantize
  50. else
  51. model_was = klass
  52. end
  53. foreign_key_was = owner.attribute_before_last_save(reflection.foreign_key)
  54. if foreign_key_was && model_was < ActiveRecord::Base
  55. update_counters_via_scope(model_was, foreign_key_was, -1)
  56. end
  57. end
  58. 1 def target_changed?
  59. owner.saved_change_to_attribute?(reflection.foreign_key)
  60. end
  61. 1 private
  62. 1 def replace(record)
  63. 42 if record
  64. 42 raise_on_type_mismatch!(record)
  65. 42 set_inverse_instance(record)
  66. 42 @updated = true
  67. end
  68. 42 replace_keys(record, force: true)
  69. 42 self.target = record
  70. end
  71. 1 def update_counters(by)
  72. if require_counter_update? && foreign_key_present?
  73. if target && !stale_target?
  74. target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
  75. else
  76. update_counters_via_scope(klass, owner._read_attribute(reflection.foreign_key), by)
  77. end
  78. end
  79. end
  80. 1 def update_counters_via_scope(klass, foreign_key, by)
  81. scope = klass.unscoped.where!(primary_key(klass) => foreign_key)
  82. scope.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
  83. end
  84. 1 def find_target?
  85. 56 !loaded? && foreign_key_present? && klass
  86. end
  87. 1 def require_counter_update?
  88. reflection.counter_cache_column && owner.persisted?
  89. end
  90. 1 def replace_keys(record, force: false)
  91. 48 target_key = record ? record._read_attribute(primary_key(record.class)) : nil
  92. 48 if force || owner[reflection.foreign_key] != target_key
  93. 42 owner[reflection.foreign_key] = target_key
  94. end
  95. end
  96. 1 def primary_key(klass)
  97. 48 reflection.association_primary_key(klass)
  98. end
  99. 1 def foreign_key_present?
  100. 6 owner._read_attribute(reflection.foreign_key)
  101. end
  102. 1 def invertible_for?(record)
  103. 48 inverse = inverse_reflection_for(record)
  104. 48 inverse && (inverse.has_one? || ActiveRecord::Base.has_many_inversing)
  105. end
  106. 1 def stale_state
  107. 250 result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
  108. 250 result && result.to_s
  109. end
  110. end
  111. end
  112. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/collection_association.rb

36.64% lines covered

232 relevant lines. 85 lines covered and 147 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module Associations
  4. # = Active Record Association Collection
  5. #
  6. # CollectionAssociation is an abstract class that provides common stuff to
  7. # ease the implementation of association proxies that represent
  8. # collections. See the class hierarchy in Association.
  9. #
  10. # CollectionAssociation:
  11. # HasManyAssociation => has_many
  12. # HasManyThroughAssociation + ThroughAssociation => has_many :through
  13. #
  14. # The CollectionAssociation class provides common methods to the collections
  15. # defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
  16. # the +:through association+ option.
  17. #
  18. # You need to be careful with assumptions regarding the target: The proxy
  19. # does not fetch records from the database until it needs them, but new
  20. # ones created with +build+ are added to the target. So, the target may be
  21. # non-empty and still lack children waiting to be read from the database.
  22. # If you look directly to the database you cannot assume that's the entire
  23. # collection because new records may have been added to the target, etc.
  24. #
  25. # If you need to work on all current children, new and existing records,
  26. # +load_target+ and the +loaded+ flag are your friends.
  27. 1 class CollectionAssociation < Association #:nodoc:
  28. # Implements the reader method, e.g. foo.items for Foo.has_many :items
  29. 1 def reader
  30. 4 if stale_target?
  31. reload
  32. end
  33. 4 @proxy ||= CollectionProxy.create(klass, self)
  34. 4 @proxy.reset_scope
  35. end
  36. # Implements the writer method, e.g. foo.items= for Foo.has_many :items
  37. 1 def writer(records)
  38. replace(records)
  39. end
  40. # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
  41. 1 def ids_reader
  42. if loaded?
  43. target.pluck(reflection.association_primary_key)
  44. elsif !target.empty?
  45. load_target.pluck(reflection.association_primary_key)
  46. else
  47. @association_ids ||= scope.pluck(reflection.association_primary_key)
  48. end
  49. end
  50. # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
  51. 1 def ids_writer(ids)
  52. primary_key = reflection.association_primary_key
  53. pk_type = klass.type_for_attribute(primary_key)
  54. ids = Array(ids).compact_blank
  55. ids.map! { |i| pk_type.cast(i) }
  56. records = klass.where(primary_key => ids).index_by do |r|
  57. r.public_send(primary_key)
  58. end.values_at(*ids).compact
  59. if records.size != ids.size
  60. found_ids = records.map { |record| record.public_send(primary_key) }
  61. not_found_ids = ids - found_ids
  62. klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
  63. else
  64. replace(records)
  65. end
  66. end
  67. 1 def reset
  68. 4 super
  69. 4 @target = []
  70. 4 @association_ids = nil
  71. end
  72. 1 def find(*args)
  73. if options[:inverse_of] && loaded?
  74. args_flatten = args.flatten
  75. model = scope.klass
  76. if args_flatten.blank?
  77. error_message = "Couldn't find #{model.name} without an ID"
  78. raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
  79. end
  80. result = find_by_scan(*args)
  81. result_size = Array(result).size
  82. if !result || result_size != args_flatten.size
  83. scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
  84. else
  85. result
  86. end
  87. else
  88. scope.find(*args)
  89. end
  90. end
  91. 1 def build(attributes = nil, &block)
  92. 1 if attributes.is_a?(Array)
  93. attributes.collect { |attr| build(attr, &block) }
  94. else
  95. 1 add_to_target(build_record(attributes, &block), replace: true)
  96. end
  97. end
  98. # Add +records+ to this association. Since +<<+ flattens its argument list
  99. # and inserts each record, +push+ and +concat+ behave identically.
  100. 1 def concat(*records)
  101. records = records.flatten
  102. if owner.new_record?
  103. load_target
  104. concat_records(records)
  105. else
  106. transaction { concat_records(records) }
  107. end
  108. end
  109. # Starts a transaction in the association class's database connection.
  110. #
  111. # class Author < ActiveRecord::Base
  112. # has_many :books
  113. # end
  114. #
  115. # Author.first.books.transaction do
  116. # # same effect as calling Book.transaction
  117. # end
  118. 1 def transaction(*args)
  119. 3 reflection.klass.transaction(*args) do
  120. 3 yield
  121. end
  122. end
  123. # Removes all records from the association without calling callbacks
  124. # on the associated records. It honors the +:dependent+ option. However
  125. # if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
  126. # deletion strategy for the association is applied.
  127. #
  128. # You can force a particular deletion strategy by passing a parameter.
  129. #
  130. # Example:
  131. #
  132. # @author.books.delete_all(:nullify)
  133. # @author.books.delete_all(:delete_all)
  134. #
  135. # See delete for more info.
  136. 1 def delete_all(dependent = nil)
  137. if dependent && ![:nullify, :delete_all].include?(dependent)
  138. raise ArgumentError, "Valid values are :nullify or :delete_all"
  139. end
  140. dependent = if dependent
  141. dependent
  142. elsif options[:dependent] == :destroy
  143. :delete_all
  144. else
  145. options[:dependent]
  146. end
  147. delete_or_nullify_all_records(dependent).tap do
  148. reset
  149. loaded!
  150. end
  151. end
  152. # Destroy all the records from this association.
  153. #
  154. # See destroy for more info.
  155. 1 def destroy_all
  156. destroy(load_target).tap do
  157. reset
  158. loaded!
  159. end
  160. end
  161. # Removes +records+ from this association calling +before_remove+ and
  162. # +after_remove+ callbacks.
  163. #
  164. # This method is abstract in the sense that +delete_records+ has to be
  165. # provided by descendants. Note this method does not imply the records
  166. # are actually removed from the database, that depends precisely on
  167. # +delete_records+. They are in any case removed from the collection.
  168. 1 def delete(*records)
  169. delete_or_destroy(records, options[:dependent])
  170. end
  171. # Deletes the +records+ and removes them from this association calling
  172. # +before_remove+ , +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
  173. #
  174. # Note that this method removes records from the database ignoring the
  175. # +:dependent+ option.
  176. 1 def destroy(*records)
  177. delete_or_destroy(records, :destroy)
  178. end
  179. # Returns the size of the collection by executing a SELECT COUNT(*)
  180. # query if the collection hasn't been loaded, and calling
  181. # <tt>collection.size</tt> if it has.
  182. #
  183. # If the collection has been already loaded +size+ and +length+ are
  184. # equivalent. If not and you are going to need the records anyway
  185. # +length+ will take one less query. Otherwise +size+ is more efficient.
  186. #
  187. # This method is abstract in the sense that it relies on
  188. # +count_records+, which is a method descendants have to provide.
  189. 1 def size
  190. if !find_target? || loaded?
  191. target.size
  192. elsif @association_ids
  193. @association_ids.size
  194. elsif !association_scope.group_values.empty?
  195. load_target.size
  196. elsif !association_scope.distinct_value && !target.empty?
  197. unsaved_records = target.select(&:new_record?)
  198. unsaved_records.size + count_records
  199. else
  200. count_records
  201. end
  202. end
  203. # Returns true if the collection is empty.
  204. #
  205. # If the collection has been loaded
  206. # it is equivalent to <tt>collection.size.zero?</tt>. If the
  207. # collection has not been loaded, it is equivalent to
  208. # <tt>!collection.exists?</tt>. If the collection has not already been
  209. # loaded and you are going to fetch the records anyway it is better to
  210. # check <tt>collection.length.zero?</tt>.
  211. 1 def empty?
  212. if loaded? || @association_ids || reflection.has_cached_counter?
  213. size.zero?
  214. else
  215. target.empty? && !scope.exists?
  216. end
  217. end
  218. # Replace this collection with +other_array+. This will perform a diff
  219. # and delete/add only records that have changed.
  220. 1 def replace(other_array)
  221. other_array.each { |val| raise_on_type_mismatch!(val) }
  222. original_target = load_target.dup
  223. if owner.new_record?
  224. replace_records(other_array, original_target)
  225. else
  226. replace_common_records_in_memory(other_array, original_target)
  227. if other_array != original_target
  228. transaction { replace_records(other_array, original_target) }
  229. else
  230. other_array
  231. end
  232. end
  233. end
  234. 1 def include?(record)
  235. if record.is_a?(reflection.klass)
  236. if record.new_record?
  237. include_in_memory?(record)
  238. else
  239. loaded? ? target.include?(record) : scope.exists?(record.id)
  240. end
  241. else
  242. false
  243. end
  244. end
  245. 1 def load_target
  246. if find_target?
  247. @target = merge_target_lists(find_target, target)
  248. end
  249. loaded!
  250. target
  251. end
  252. 1 def add_to_target(record, skip_callbacks: false, replace: false, &block)
  253. 4 if replace || association_scope.distinct_value
  254. 1 index = @target.index(record)
  255. end
  256. 4 replace_on_target(record, index, skip_callbacks, &block)
  257. end
  258. 1 def target=(record)
  259. return super unless ActiveRecord::Base.has_many_inversing
  260. case record
  261. when Array
  262. super
  263. else
  264. add_to_target(record, skip_callbacks: true, replace: true)
  265. end
  266. end
  267. 1 def scope
  268. 8 scope = super
  269. 8 scope.none! if null_scope?
  270. 8 scope
  271. end
  272. 1 def null_scope?
  273. 8 owner.new_record? && !foreign_key_present?
  274. end
  275. 1 def find_from_target?
  276. loaded? ||
  277. owner.strict_loading? ||
  278. reflection.strict_loading? ||
  279. owner.new_record? ||
  280. target.any? { |record| record.new_record? || record.changed? }
  281. end
  282. 1 private
  283. # We have some records loaded from the database (persisted) and some that are
  284. # in-memory (memory). The same record may be represented in the persisted array
  285. # and in the memory array.
  286. #
  287. # So the task of this method is to merge them according to the following rules:
  288. #
  289. # * The final array must not have duplicates
  290. # * The order of the persisted array is to be preserved
  291. # * Any changes made to attributes on objects in the memory array are to be preserved
  292. # * Otherwise, attributes should have the value found in the database
  293. 1 def merge_target_lists(persisted, memory)
  294. return persisted if memory.empty?
  295. return memory if persisted.empty?
  296. persisted.map! do |record|
  297. if mem_record = memory.delete(record)
  298. ((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
  299. mem_record[name] = record[name]
  300. end
  301. mem_record
  302. else
  303. record
  304. end
  305. end
  306. persisted + memory
  307. end
  308. 1 def _create_record(attributes, raise = false, &block)
  309. 3 unless owner.persisted?
  310. raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
  311. end
  312. 3 if attributes.is_a?(Array)
  313. attributes.collect { |attr| _create_record(attr, raise, &block) }
  314. else
  315. 3 record = build_record(attributes, &block)
  316. 3 transaction do
  317. 3 result = nil
  318. 3 add_to_target(record) do
  319. 3 result = insert_record(record, true, raise) {
  320. 3 @_was_loaded = loaded?
  321. }
  322. end
  323. 3 raise ActiveRecord::Rollback unless result
  324. end
  325. 3 record
  326. end
  327. end
  328. # Do the relevant stuff to insert the given record into the association collection.
  329. 1 def insert_record(record, validate = true, raise = false, &block)
  330. 3 if raise
  331. record.save!(validate: validate, &block)
  332. else
  333. 3 record.save(validate: validate, &block)
  334. end
  335. end
  336. 1 def delete_or_destroy(records, method)
  337. return if records.empty?
  338. records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
  339. records = records.flatten
  340. records.each { |record| raise_on_type_mismatch!(record) }
  341. existing_records = records.reject(&:new_record?)
  342. if existing_records.empty?
  343. remove_records(existing_records, records, method)
  344. else
  345. transaction { remove_records(existing_records, records, method) }
  346. end
  347. end
  348. 1 def remove_records(existing_records, records, method)
  349. catch(:abort) do
  350. records.each { |record| callback(:before_remove, record) }
  351. end || return
  352. delete_records(existing_records, method) if existing_records.any?
  353. @target -= records
  354. @association_ids = nil
  355. records.each { |record| callback(:after_remove, record) }
  356. end
  357. # Delete the given records from the association,
  358. # using one of the methods +:destroy+, +:delete_all+
  359. # or +:nullify+ (or +nil+, in which case a default is used).
  360. 1 def delete_records(records, method)
  361. raise NotImplementedError
  362. end
  363. 1 def replace_records(new_target, original_target)
  364. delete(difference(target, new_target))
  365. unless concat(difference(new_target, target))
  366. @target = original_target
  367. raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
  368. "new records could not be saved."
  369. end
  370. target
  371. end
  372. 1 def replace_common_records_in_memory(new_target, original_target)
  373. common_records = intersection(new_target, original_target)
  374. common_records.each do |record|
  375. skip_callbacks = true
  376. replace_on_target(record, @target.index(record), skip_callbacks)
  377. end
  378. end
  379. 1 def concat_records(records, raise = false)
  380. result = true
  381. records.each do |record|
  382. raise_on_type_mismatch!(record)
  383. add_to_target(record) do
  384. unless owner.new_record?
  385. result &&= insert_record(record, true, raise) {
  386. @_was_loaded = loaded?
  387. }
  388. end
  389. end
  390. end
  391. raise ActiveRecord::Rollback unless result
  392. records
  393. end
  394. 1 def replace_on_target(record, index, skip_callbacks)
  395. catch(:abort) do
  396. 4 callback(:before_add, record)
  397. 4 end || return unless skip_callbacks
  398. 4 set_inverse_instance(record)
  399. 4 @_was_loaded = true
  400. 4 yield(record) if block_given?
  401. 4 if index
  402. target[index] = record
  403. 4 elsif @_was_loaded || !loaded?
  404. 4 @association_ids = nil
  405. 4 target << record
  406. end
  407. 4 callback(:after_add, record) unless skip_callbacks
  408. 4 record
  409. ensure
  410. 4 @_was_loaded = nil
  411. end
  412. 1 def callback(method, record)
  413. 8 callbacks_for(method).each do |callback|
  414. callback.call(method, owner, record)
  415. end
  416. end
  417. 1 def callbacks_for(callback_name)
  418. 8 full_callback_name = "#{callback_name}_for_#{reflection.name}"
  419. 8 owner.class.send(full_callback_name)
  420. end
  421. 1 def include_in_memory?(record)
  422. if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
  423. assoc = owner.association(reflection.through_reflection.name)
  424. assoc.reader.any? { |source|
  425. target_reflection = source.send(reflection.source_reflection.name)
  426. target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
  427. } || target.include?(record)
  428. else
  429. target.include?(record)
  430. end
  431. end
  432. # If the :inverse_of option has been
  433. # specified, then #find scans the entire collection.
  434. 1 def find_by_scan(*args)
  435. expects_array = args.first.kind_of?(Array)
  436. ids = args.flatten.compact.map(&:to_s).uniq
  437. if ids.size == 1
  438. id = ids.first
  439. record = load_target.detect { |r| id == r.id.to_s }
  440. expects_array ? [ record ] : record
  441. else
  442. load_target.select { |r| ids.include?(r.id.to_s) }
  443. end
  444. end
  445. end
  446. end
  447. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/foreign_association.rb

58.82% lines covered

17 relevant lines. 10 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord::Associations
  3. 1 module ForeignAssociation # :nodoc:
  4. 1 def foreign_key_present?
  5. if reflection.klass.primary_key
  6. owner.attribute_present?(reflection.active_record_primary_key)
  7. else
  8. false
  9. end
  10. end
  11. 1 def nullified_owner_attributes
  12. Hash.new.tap do |attrs|
  13. attrs[reflection.foreign_key] = nil
  14. attrs[reflection.type] = nil if reflection.type.present?
  15. end
  16. end
  17. 1 private
  18. # Sets the owner attributes on the given record
  19. 1 def set_owner_attributes(record)
  20. 3 return if options[:through]
  21. 3 key = owner._read_attribute(reflection.join_foreign_key)
  22. 3 record._write_attribute(reflection.join_primary_key, key)
  23. 3 if reflection.type
  24. record._write_attribute(reflection.type, owner.class.polymorphic_name)
  25. end
  26. end
  27. end
  28. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/has_many_association.rb

38.24% lines covered

68 relevant lines. 26 lines covered and 42 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module Associations
  4. # = Active Record Has Many Association
  5. # This is the proxy that handles a has many association.
  6. #
  7. # If the association has a <tt>:through</tt> option further specialization
  8. # is provided by its child HasManyThroughAssociation.
  9. 1 class HasManyAssociation < CollectionAssociation #:nodoc:
  10. 1 include ForeignAssociation
  11. 1 def handle_dependency
  12. case options[:dependent]
  13. when :restrict_with_exception
  14. raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
  15. when :restrict_with_error
  16. unless empty?
  17. record = owner.class.human_attribute_name(reflection.name).downcase
  18. owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)
  19. throw(:abort)
  20. end
  21. when :destroy
  22. # No point in executing the counter update since we're going to destroy the parent anyway
  23. load_target.each { |t| t.destroyed_by_association = reflection }
  24. destroy_all
  25. when :destroy_async
  26. load_target.each do |t|
  27. t.destroyed_by_association = reflection
  28. end
  29. unless target.empty?
  30. association_class = target.first.class
  31. primary_key_column = association_class.primary_key.to_sym
  32. ids = target.collect do |assoc|
  33. assoc.public_send(primary_key_column)
  34. end
  35. enqueue_destroy_association(
  36. owner_model_name: owner.class.to_s,
  37. owner_id: owner.id,
  38. association_class: reflection.klass.to_s,
  39. association_ids: ids,
  40. association_primary_key_column: primary_key_column,
  41. ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
  42. )
  43. end
  44. else
  45. delete_all
  46. end
  47. end
  48. 1 def insert_record(record, validate = true, raise = false)
  49. 3 set_owner_attributes(record)
  50. 3 super
  51. end
  52. 1 private
  53. # Returns the number of records in this collection.
  54. #
  55. # If the association has a counter cache it gets that value. Otherwise
  56. # it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
  57. # there's one. Some configuration options like :group make it impossible
  58. # to do an SQL count, in those cases the array count will be used.
  59. #
  60. # That does not depend on whether the collection has already been loaded
  61. # or not. The +size+ method is the one that takes the loaded flag into
  62. # account and delegates to +count_records+ if needed.
  63. #
  64. # If the collection is empty the target is set to an empty array and
  65. # the loaded flag is set to true as well.
  66. 1 def count_records
  67. count = if reflection.has_cached_counter?
  68. owner.read_attribute(reflection.counter_cache_column).to_i
  69. else
  70. scope.count(:all)
  71. end
  72. # If there's nothing in the database and @target has no new records
  73. # we are certain the current target is an empty array. This is a
  74. # documented side-effect of the method that may avoid an extra SELECT.
  75. loaded! if count == 0
  76. [association_scope.limit_value, count].compact.min
  77. end
  78. 1 def update_counter(difference, reflection = reflection())
  79. if reflection.has_cached_counter?
  80. owner.increment!(reflection.counter_cache_column, difference)
  81. end
  82. end
  83. 1 def update_counter_in_memory(difference, reflection = reflection())
  84. 3 if reflection.counter_must_be_updated_by_has_many?
  85. counter = reflection.counter_cache_column
  86. owner.increment(counter, difference)
  87. owner.send(:"clear_#{counter}_change")
  88. end
  89. end
  90. 1 def delete_count(method, scope)
  91. if method == :delete_all
  92. scope.delete_all
  93. else
  94. scope.update_all(nullified_owner_attributes)
  95. end
  96. end
  97. 1 def delete_or_nullify_all_records(method)
  98. count = delete_count(method, scope)
  99. update_counter(-count)
  100. count
  101. end
  102. # Deletes the records according to the <tt>:dependent</tt> option.
  103. 1 def delete_records(records, method)
  104. if method == :destroy
  105. records.each(&:destroy!)
  106. update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
  107. else
  108. scope = self.scope.where(reflection.klass.primary_key => records)
  109. update_counter(-delete_count(method, scope))
  110. end
  111. end
  112. 1 def concat_records(records, *)
  113. update_counter_if_success(super, records.length)
  114. end
  115. 1 def _create_record(attributes, *)
  116. 3 if attributes.is_a?(Array)
  117. super
  118. else
  119. 3 update_counter_if_success(super, 1)
  120. end
  121. end
  122. 1 def update_counter_if_success(saved_successfully, difference)
  123. 3 if saved_successfully
  124. 3 update_counter_in_memory(difference)
  125. end
  126. 3 saved_successfully
  127. end
  128. 1 def difference(a, b)
  129. a - b
  130. end
  131. 1 def intersection(a, b)
  132. a & b
  133. end
  134. end
  135. end
  136. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/has_many_through_association.rb

31.9% lines covered

116 relevant lines. 37 lines covered and 79 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module Associations
  4. # = Active Record Has Many Through Association
  5. 1 class HasManyThroughAssociation < HasManyAssociation #:nodoc:
  6. 1 include ThroughAssociation
  7. 1 def initialize(owner, reflection)
  8. 1 super
  9. 1 @through_records = {}.compare_by_identity
  10. end
  11. 1 def concat(*records)
  12. unless owner.new_record?
  13. records.flatten.each do |record|
  14. raise_on_type_mismatch!(record)
  15. end
  16. end
  17. super
  18. end
  19. 1 def insert_record(record, validate = true, raise = false)
  20. ensure_not_nested
  21. if record.new_record? || record.has_changes_to_save?
  22. return unless super
  23. end
  24. save_through_record(record)
  25. record
  26. end
  27. 1 private
  28. 1 def concat_records(records)
  29. ensure_not_nested
  30. records = super(records, true)
  31. if owner.new_record? && records
  32. records.flatten.each do |record|
  33. build_through_record(record)
  34. end
  35. end
  36. records
  37. end
  38. # The through record (built with build_record) is temporarily cached
  39. # so that it may be reused if insert_record is subsequently called.
  40. #
  41. # However, after insert_record has been called, the cache is cleared in
  42. # order to allow multiple instances of the same record in an association.
  43. 1 def build_through_record(record)
  44. @through_records[record] ||= begin
  45. ensure_mutable
  46. attributes = through_scope_attributes
  47. attributes[source_reflection.name] = record
  48. attributes[source_reflection.foreign_type] = options[:source_type] if options[:source_type]
  49. through_association.build(attributes)
  50. end
  51. end
  52. 1 attr_reader :through_scope
  53. 1 def through_scope_attributes
  54. scope = through_scope || self.scope
  55. scope.where_values_hash(through_association.reflection.name.to_s).
  56. except!(through_association.reflection.foreign_key,
  57. through_association.reflection.klass.inheritance_column)
  58. end
  59. 1 def save_through_record(record)
  60. association = build_through_record(record)
  61. if association.changed?
  62. association.save!
  63. end
  64. ensure
  65. @through_records.delete(record)
  66. end
  67. 1 def build_record(attributes)
  68. 1 ensure_not_nested
  69. 1 @through_scope = scope
  70. 1 record = super
  71. 1 inverse = source_reflection.inverse_of
  72. 1 if inverse
  73. if inverse.collection?
  74. record.send(inverse.name) << build_through_record(record)
  75. elsif inverse.has_one?
  76. record.send("#{inverse.name}=", build_through_record(record))
  77. end
  78. end
  79. 1 record
  80. ensure
  81. 1 @through_scope = nil
  82. end
  83. 1 def remove_records(existing_records, records, method)
  84. super
  85. delete_through_records(records)
  86. end
  87. 1 def target_reflection_has_associated_record?
  88. !(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
  89. end
  90. 1 def update_through_counter?(method)
  91. case method
  92. when :destroy
  93. !through_reflection.inverse_updates_counter_cache?
  94. when :nullify
  95. false
  96. else
  97. true
  98. end
  99. end
  100. 1 def delete_or_nullify_all_records(method)
  101. delete_records(load_target, method)
  102. end
  103. 1 def delete_records(records, method)
  104. ensure_not_nested
  105. scope = through_association.scope
  106. scope.where! construct_join_attributes(*records)
  107. scope = scope.where(through_scope_attributes)
  108. case method
  109. when :destroy
  110. if scope.klass.primary_key
  111. count = scope.destroy_all.count(&:destroyed?)
  112. else
  113. scope.each(&:_run_destroy_callbacks)
  114. count = scope.delete_all
  115. end
  116. when :nullify
  117. count = scope.update_all(source_reflection.foreign_key => nil)
  118. else
  119. count = scope.delete_all
  120. end
  121. delete_through_records(records)
  122. if source_reflection.options[:counter_cache] && method != :destroy
  123. counter = source_reflection.counter_cache_column
  124. klass.decrement_counter counter, records.map(&:id)
  125. end
  126. if through_reflection.collection? && update_through_counter?(method)
  127. update_counter(-count, through_reflection)
  128. else
  129. update_counter(-count)
  130. end
  131. count
  132. end
  133. 1 def difference(a, b)
  134. distribution = distribution(b)
  135. a.reject { |record| mark_occurrence(distribution, record) }
  136. end
  137. 1 def intersection(a, b)
  138. distribution = distribution(b)
  139. a.select { |record| mark_occurrence(distribution, record) }
  140. end
  141. 1 def mark_occurrence(distribution, record)
  142. distribution[record] > 0 && distribution[record] -= 1
  143. end
  144. 1 def distribution(array)
  145. array.each_with_object(Hash.new(0)) do |record, distribution|
  146. distribution[record] += 1
  147. end
  148. end
  149. 1 def through_records_for(record)
  150. attributes = construct_join_attributes(record)
  151. candidates = Array.wrap(through_association.target)
  152. candidates.find_all do |c|
  153. attributes.all? do |key, value|
  154. c.public_send(key) == value
  155. end
  156. end
  157. end
  158. 1 def delete_through_records(records)
  159. records.each do |record|
  160. through_records = through_records_for(record)
  161. if through_reflection.collection?
  162. through_records.each { |r| through_association.target.delete(r) }
  163. else
  164. if through_records.include?(through_association.target)
  165. through_association.target = nil
  166. end
  167. end
  168. @through_records.delete(record)
  169. end
  170. end
  171. 1 def find_target
  172. return [] unless target_reflection_has_associated_record?
  173. super
  174. end
  175. # NOTE - not sure that we can actually cope with inverses here
  176. 1 def invertible_for?(record)
  177. 2 false
  178. end
  179. end
  180. end
  181. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/singular_association.rb

58.06% lines covered

31 relevant lines. 18 lines covered and 13 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module Associations
  4. 1 class SingularAssociation < Association #:nodoc:
  5. # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
  6. 1 def reader
  7. 67 if !loaded? || stale_target?
  8. 6 reload
  9. end
  10. 67 target
  11. end
  12. # Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
  13. 1 def writer(record)
  14. 42 replace(record)
  15. end
  16. 1 def build(attributes = nil, &block)
  17. record = build_record(attributes, &block)
  18. set_new_record(record)
  19. record
  20. end
  21. # Implements the reload reader method, e.g. foo.reload_bar for
  22. # Foo.has_one :bar
  23. 1 def force_reload_reader
  24. reload(true)
  25. target
  26. end
  27. 1 private
  28. 1 def scope_for_create
  29. super.except!(klass.primary_key)
  30. end
  31. 1 def find_target
  32. 6 super.first
  33. end
  34. 1 def replace(record)
  35. raise NotImplementedError, "Subclasses must implement a replace(record) method"
  36. end
  37. 1 def set_new_record(record)
  38. replace(record)
  39. end
  40. 1 def _create_record(attributes, raise_error = false, &block)
  41. record = build_record(attributes, &block)
  42. saved = record.save
  43. set_new_record(record)
  44. raise RecordInvalid.new(record) if !saved && raise_error
  45. record
  46. end
  47. end
  48. end
  49. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/associations/through_association.rb

58.82% lines covered

51 relevant lines. 30 lines covered and 21 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module Associations
  4. # = Active Record Through Association
  5. 1 module ThroughAssociation #:nodoc:
  6. 1 delegate :source_reflection, to: :reflection
  7. 1 private
  8. 1 def through_reflection
  9. 3 @through_reflection ||= begin
  10. 1 refl = reflection.through_reflection
  11. 1 while refl.through_reflection?
  12. refl = refl.through_reflection
  13. end
  14. 1 refl
  15. end
  16. end
  17. 1 def through_association
  18. 1 @through_association ||= owner.association(through_reflection.name)
  19. end
  20. # We merge in these scopes for two reasons:
  21. #
  22. # 1. To get the default_scope conditions for any of the other reflections in the chain
  23. # 2. To get the type conditions for any STI models in the chain
  24. 1 def target_scope
  25. 2 scope = super
  26. 2 reflection.chain.drop(1).each do |reflection|
  27. 2 relation = reflection.klass.scope_for_association
  28. 2 scope.merge!(
  29. relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
  30. )
  31. end
  32. 2 scope
  33. end
  34. # Construct attributes for :through pointing to owner and associate. This is used by the
  35. # methods which create and delete records on the association.
  36. #
  37. # We only support indirectly modifying through associations which have a belongs_to source.
  38. # This is the "has_many :tags, through: :taggings" situation, where the join model
  39. # typically has a belongs_to on both side. In other words, associations which could also
  40. # be represented as has_and_belongs_to_many associations.
  41. #
  42. # We do not support creating/deleting records on the association where the source has
  43. # some other type, because this opens up a whole can of worms, and in basically any
  44. # situation it is more natural for the user to just create or modify their join records
  45. # directly as required.
  46. 1 def construct_join_attributes(*records)
  47. ensure_mutable
  48. association_primary_key = source_reflection.association_primary_key(reflection.klass)
  49. if association_primary_key == reflection.klass.primary_key && !options[:source_type]
  50. join_attributes = { source_reflection.name => records }
  51. else
  52. join_attributes = {
  53. source_reflection.foreign_key => records.map(&association_primary_key.to_sym)
  54. }
  55. end
  56. if options[:source_type]
  57. join_attributes[source_reflection.foreign_type] = [ options[:source_type] ]
  58. end
  59. if records.count == 1
  60. join_attributes.transform_values!(&:first)
  61. else
  62. join_attributes
  63. end
  64. end
  65. # Note: this does not capture all cases, for example it would be crazy to try to
  66. # properly support stale-checking for nested associations.
  67. 1 def stale_state
  68. if through_reflection.belongs_to?
  69. owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
  70. end
  71. end
  72. 1 def foreign_key_present?
  73. 2 through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
  74. end
  75. 1 def ensure_mutable
  76. unless source_reflection.belongs_to?
  77. if reflection.has_one?
  78. raise HasOneThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
  79. else
  80. raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
  81. end
  82. end
  83. end
  84. 1 def ensure_not_nested
  85. 1 if reflection.nested?
  86. if reflection.has_one?
  87. raise HasOneThroughNestedAssociationsAreReadonly.new(owner, reflection)
  88. else
  89. raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
  90. end
  91. end
  92. end
  93. 1 def build_record(attributes)
  94. 1 inverse = source_reflection.inverse_of
  95. 1 target = through_association.target
  96. 1 if inverse && target && !target.is_a?(Array)
  97. attributes[inverse.foreign_key] = target.id
  98. end
  99. 1 super
  100. end
  101. end
  102. end
  103. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/connection_adapters/abstract/transaction.rb

80.2% lines covered

202 relevant lines. 162 lines covered and 40 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module ConnectionAdapters
  4. 1 class TransactionState
  5. 1 def initialize(state = nil)
  6. 95 @state = state
  7. 95 @children = nil
  8. end
  9. 1 def add_child(state)
  10. 79 @children ||= []
  11. 79 @children << state
  12. end
  13. 1 def finalized?
  14. @state
  15. end
  16. 1 def committed?
  17. @state == :committed || @state == :fully_committed
  18. end
  19. 1 def fully_committed?
  20. @state == :fully_committed
  21. end
  22. 1 def rolledback?
  23. @state == :rolledback || @state == :fully_rolledback
  24. end
  25. 1 def fully_rolledback?
  26. @state == :fully_rolledback
  27. end
  28. 1 def fully_completed?
  29. completed?
  30. end
  31. 1 def completed?
  32. committed? || rolledback?
  33. end
  34. 1 def rollback!
  35. 79 @children&.each { |c| c.rollback! }
  36. 79 @state = :rolledback
  37. end
  38. 1 def full_rollback!
  39. 95 @children&.each { |c| c.rollback! }
  40. 16 @state = :fully_rolledback
  41. end
  42. 1 def commit!
  43. 79 @state = :committed
  44. end
  45. 1 def full_commit!
  46. @state = :fully_committed
  47. end
  48. 1 def nullify!
  49. @state = nil
  50. end
  51. end
  52. 1 class NullTransaction #:nodoc:
  53. 1 def initialize; end
  54. 1 def state; end
  55. 1 def closed?; true; end
  56. 40 def open?; false; end
  57. 17 def joinable?; false; end
  58. 1 def add_record(record, _ = true); end
  59. end
  60. 1 class Transaction #:nodoc:
  61. 1 attr_reader :connection, :state, :savepoint_name, :isolation_level
  62. 1 attr_accessor :written
  63. 1 def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
  64. 95 @connection = connection
  65. 95 @state = TransactionState.new
  66. 95 @records = nil
  67. 95 @isolation_level = isolation
  68. 95 @materialized = false
  69. 95 @joinable = joinable
  70. 95 @run_commit_callbacks = run_commit_callbacks
  71. 95 @lazy_enrollment_records = nil
  72. end
  73. 1 def add_record(record, ensure_finalize = true)
  74. 79 @records ||= []
  75. 79 if ensure_finalize
  76. @records << record
  77. else
  78. 79 @lazy_enrollment_records ||= ObjectSpace::WeakMap.new
  79. 79 @lazy_enrollment_records[record] = record
  80. end
  81. end
  82. 1 def records
  83. 332 if @lazy_enrollment_records
  84. 79 @records.concat @lazy_enrollment_records.values
  85. 79 @lazy_enrollment_records = nil
  86. end
  87. 332 @records
  88. end
  89. 1 def materialize!
  90. 95 @materialized = true
  91. end
  92. 1 def materialized?
  93. 253 @materialized
  94. end
  95. 1 def rollback_records
  96. 16 return unless records
  97. ite = records.uniq(&:__id__)
  98. already_run_callbacks = {}
  99. while record = ite.shift
  100. trigger_callbacks = record.trigger_transactional_callbacks?
  101. should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
  102. already_run_callbacks[record] ||= trigger_callbacks
  103. record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
  104. end
  105. ensure
  106. 16 ite&.each do |i|
  107. i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
  108. end
  109. end
  110. 1 def before_commit_records
  111. 79 records.uniq.each(&:before_committed!) if records && @run_commit_callbacks
  112. end
  113. 1 def commit_records
  114. 79 return unless records
  115. 79 ite = records.uniq(&:__id__)
  116. 79 already_run_callbacks = {}
  117. 79 while record = ite.shift
  118. 79 if @run_commit_callbacks
  119. 79 trigger_callbacks = record.trigger_transactional_callbacks?
  120. 79 should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
  121. 79 already_run_callbacks[record] ||= trigger_callbacks
  122. 79 record.committed!(should_run_callbacks: should_run_callbacks)
  123. else
  124. # if not running callbacks, only adds the record to the parent transaction
  125. connection.add_transaction_record(record)
  126. end
  127. end
  128. ensure
  129. 79 ite&.each { |i| i.committed!(should_run_callbacks: false) }
  130. end
  131. 1 def full_rollback?; true; end
  132. 162 def joinable?; @joinable; end
  133. 368 def closed?; false; end
  134. 368 def open?; !closed?; end
  135. end
  136. 1 class SavepointTransaction < Transaction
  137. 1 def initialize(connection, savepoint_name, parent_transaction, **options)
  138. 79 super(connection, **options)
  139. 79 parent_transaction.state.add_child(@state)
  140. 79 if isolation_level
  141. raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
  142. end
  143. 79 @savepoint_name = savepoint_name
  144. end
  145. 1 def materialize!
  146. 79 connection.create_savepoint(savepoint_name)
  147. 79 super
  148. end
  149. 1 def rollback
  150. connection.rollback_to_savepoint(savepoint_name) if materialized?
  151. @state.rollback!
  152. end
  153. 1 def commit
  154. 79 connection.release_savepoint(savepoint_name) if materialized?
  155. 79 @state.commit!
  156. end
  157. 1 def full_rollback?; false; end
  158. end
  159. 1 class RealTransaction < Transaction
  160. 1 def materialize!
  161. 16 if isolation_level
  162. connection.begin_isolated_db_transaction(isolation_level)
  163. else
  164. 16 connection.begin_db_transaction
  165. end
  166. 16 super
  167. end
  168. 1 def rollback
  169. 16 connection.rollback_db_transaction if materialized?
  170. 16 @state.full_rollback!
  171. end
  172. 1 def commit
  173. connection.commit_db_transaction if materialized?
  174. @state.full_commit!
  175. end
  176. end
  177. 1 class TransactionManager #:nodoc:
  178. 1 def initialize(connection)
  179. 3 @stack = []
  180. 3 @connection = connection
  181. 3 @has_unmaterialized_transactions = false
  182. 3 @materializing_transactions = false
  183. 3 @lazy_transactions_enabled = true
  184. end
  185. 1 def begin_transaction(isolation: nil, joinable: true, _lazy: true)
  186. 95 @connection.lock.synchronize do
  187. 95 run_commit_callbacks = !current_transaction.joinable?
  188. transaction =
  189. 95 if @stack.empty?
  190. 16 RealTransaction.new(
  191. @connection,
  192. isolation: isolation,
  193. joinable: joinable,
  194. run_commit_callbacks: run_commit_callbacks
  195. )
  196. else
  197. 79 SavepointTransaction.new(
  198. @connection,
  199. "active_record_#{@stack.size}",
  200. @stack.last,
  201. isolation: isolation,
  202. joinable: joinable,
  203. run_commit_callbacks: run_commit_callbacks
  204. )
  205. end
  206. 95 if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && _lazy
  207. 79 @has_unmaterialized_transactions = true
  208. else
  209. 16 transaction.materialize!
  210. end
  211. 95 @stack.push(transaction)
  212. 95 transaction
  213. end
  214. end
  215. 1 def disable_lazy_transactions!
  216. materialize_transactions
  217. @lazy_transactions_enabled = false
  218. end
  219. 1 def enable_lazy_transactions!
  220. 17 @lazy_transactions_enabled = true
  221. end
  222. 1 def lazy_transactions_enabled?
  223. 95 @lazy_transactions_enabled
  224. end
  225. 1 def materialize_transactions
  226. 309 return if @materializing_transactions
  227. 230 return unless @has_unmaterialized_transactions
  228. 79 @connection.lock.synchronize do
  229. begin
  230. 79 @materializing_transactions = true
  231. 237 @stack.each { |t| t.materialize! unless t.materialized? }
  232. ensure
  233. 79 @materializing_transactions = false
  234. end
  235. 79 @has_unmaterialized_transactions = false
  236. end
  237. end
  238. 1 def commit_transaction
  239. 79 @connection.lock.synchronize do
  240. 79 transaction = @stack.last
  241. begin
  242. 79 transaction.before_commit_records
  243. ensure
  244. 79 @stack.pop
  245. end
  246. 79 transaction.commit
  247. 79 transaction.commit_records
  248. end
  249. end
  250. 1 def rollback_transaction(transaction = nil)
  251. 16 @connection.lock.synchronize do
  252. 16 transaction ||= @stack.pop
  253. 16 transaction.rollback
  254. 16 transaction.rollback_records
  255. end
  256. end
  257. 1 def within_new_transaction(isolation: nil, joinable: true)
  258. 79 @connection.lock.synchronize do
  259. 79 transaction = begin_transaction(isolation: isolation, joinable: joinable)
  260. 79 ret = yield
  261. 79 completed = true
  262. 79 ret
  263. rescue Exception => error
  264. if transaction
  265. rollback_transaction
  266. after_failure_actions(transaction, error)
  267. end
  268. raise
  269. ensure
  270. 79 if transaction
  271. 79 if error
  272. # @connection still holds an open transaction, so we must not
  273. # put it back in the pool for reuse
  274. @connection.throw_away! unless transaction.state.rolledback?
  275. else
  276. 79 if Thread.current.status == "aborting"
  277. rollback_transaction
  278. else
  279. 79 if !completed && transaction.written
  280. ActiveSupport::Deprecation.warn(<<~EOW)
  281. Using `return`, `break` or `throw` to exit a transaction block is
  282. deprecated without replacement. If the `throw` came from
  283. `Timeout.timeout(duration)`, pass an exception class as a second
  284. argument so it doesn't use `throw` to abort its block. This results
  285. in the transaction being committed, but in the next release of Rails
  286. it will rollback.
  287. EOW
  288. end
  289. begin
  290. 79 commit_transaction
  291. rescue Exception
  292. rollback_transaction(transaction) unless transaction.state.completed?
  293. raise
  294. end
  295. end
  296. end
  297. end
  298. end
  299. end
  300. 1 def open_transactions
  301. @stack.size
  302. end
  303. 1 def current_transaction
  304. 662 @stack.last || NULL_TRANSACTION
  305. end
  306. 1 private
  307. 1 NULL_TRANSACTION = NullTransaction.new
  308. # Deallocate invalidated prepared statements outside of the transaction
  309. 1 def after_failure_actions(transaction, error)
  310. return unless transaction.is_a?(RealTransaction)
  311. return unless error.is_a?(ActiveRecord::PreparedStatementCacheExpired)
  312. @connection.clear_cache!
  313. end
  314. end
  315. end
  316. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/file.rb

40.54% lines covered

37 relevant lines. 15 lines covered and 22 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/configuration_file"
  3. 1 module ActiveRecord
  4. 1 class FixtureSet
  5. 1 class File # :nodoc:
  6. 1 include Enumerable
  7. ##
  8. # Open a fixture file named +file+. When called with a block, the block
  9. # is called with the filehandle and the filehandle is automatically closed
  10. # when the block finishes.
  11. 1 def self.open(file)
  12. x = new file
  13. block_given? ? yield(x) : x
  14. end
  15. 1 def initialize(file)
  16. @file = file
  17. end
  18. 1 def each(&block)
  19. rows.each(&block)
  20. end
  21. 1 def model_class
  22. config_row["model_class"]
  23. end
  24. 1 def ignored_fixtures
  25. config_row["ignore"]
  26. end
  27. 1 private
  28. 1 def rows
  29. @rows ||= raw_rows.reject { |fixture_name, _| fixture_name == "_fixture" }
  30. end
  31. 1 def config_row
  32. @config_row ||= begin
  33. row = raw_rows.find { |fixture_name, _| fixture_name == "_fixture" }
  34. if row
  35. row.last
  36. else
  37. { 'model_class': nil, 'ignore': nil }
  38. end
  39. end
  40. end
  41. 1 def raw_rows
  42. @raw_rows ||= begin
  43. data = ActiveSupport::ConfigurationFile.parse(@file, context:
  44. ActiveRecord::FixtureSet::RenderContext.create_subclass.new.get_binding)
  45. data ? validate(data).to_a : []
  46. rescue RuntimeError => error
  47. raise Fixture::FormatError, error.message
  48. end
  49. end
  50. # Validate our unmarshalled data.
  51. 1 def validate(data)
  52. unless Hash === data || YAML::Omap === data
  53. raise Fixture::FormatError, "fixture is not a hash: #{@file}"
  54. end
  55. invalid = data.reject { |_, row| Hash === row }
  56. if invalid.any?
  57. raise Fixture::FormatError, "fixture key is not a hash: #{@file}, keys: #{invalid.keys.inspect}"
  58. end
  59. data
  60. end
  61. end
  62. end
  63. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/model_metadata.rb

56.25% lines covered

16 relevant lines. 9 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class FixtureSet
  4. 1 class ModelMetadata # :nodoc:
  5. 1 def initialize(model_class)
  6. @model_class = model_class
  7. end
  8. 1 def primary_key_name
  9. @primary_key_name ||= @model_class && @model_class.primary_key
  10. end
  11. 1 def primary_key_type
  12. @primary_key_type ||= @model_class && @model_class.type_for_attribute(@model_class.primary_key).type
  13. end
  14. 1 def has_primary_key_column?
  15. @has_primary_key_column ||= primary_key_name &&
  16. @model_class.columns.any? { |col| col.name == primary_key_name }
  17. end
  18. 1 def timestamp_column_names
  19. @model_class.all_timestamp_attributes_in_model
  20. end
  21. 1 def inheritance_column_name
  22. @inheritance_column_name ||= @model_class && @model_class.inheritance_column
  23. end
  24. end
  25. end
  26. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/render_context.rb

28.57% lines covered

7 relevant lines. 2 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. # NOTE: This class has to be defined in compact style in
  3. # order for rendering context subclassing to work correctly.
  4. 1 class ActiveRecord::FixtureSet::RenderContext # :nodoc:
  5. 1 def self.create_subclass
  6. Class.new(ActiveRecord::FixtureSet.context_class) do
  7. def get_binding
  8. binding()
  9. end
  10. def binary(path)
  11. %(!!binary "#{Base64.strict_encode64(File.binread(path))}")
  12. end
  13. end
  14. end
  15. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/table_row.rb

32.05% lines covered

78 relevant lines. 25 lines covered and 53 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class FixtureSet
  4. 1 class TableRow # :nodoc:
  5. 1 class ReflectionProxy # :nodoc:
  6. 1 def initialize(association)
  7. @association = association
  8. end
  9. 1 def join_table
  10. @association.join_table
  11. end
  12. 1 def name
  13. @association.name
  14. end
  15. 1 def primary_key_type
  16. @association.klass.type_for_attribute(@association.klass.primary_key).type
  17. end
  18. end
  19. 1 class HasManyThroughProxy < ReflectionProxy # :nodoc:
  20. 1 def rhs_key
  21. @association.foreign_key
  22. end
  23. 1 def lhs_key
  24. @association.through_reflection.foreign_key
  25. end
  26. 1 def join_table
  27. @association.through_reflection.table_name
  28. end
  29. end
  30. 1 def initialize(fixture, table_rows:, label:, now:)
  31. @table_rows = table_rows
  32. @label = label
  33. @now = now
  34. @row = fixture.to_hash
  35. fill_row_model_attributes
  36. end
  37. 1 def to_hash
  38. @row
  39. end
  40. 1 private
  41. 1 def model_metadata
  42. @table_rows.model_metadata
  43. end
  44. 1 def model_class
  45. @table_rows.model_class
  46. end
  47. 1 def fill_row_model_attributes
  48. return unless model_class
  49. fill_timestamps
  50. interpolate_label
  51. generate_primary_key
  52. resolve_enums
  53. resolve_sti_reflections
  54. end
  55. 1 def reflection_class
  56. @reflection_class ||= if @row.include?(model_metadata.inheritance_column_name)
  57. @row[model_metadata.inheritance_column_name].constantize rescue model_class
  58. else
  59. model_class
  60. end
  61. end
  62. 1 def fill_timestamps
  63. # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
  64. if model_class.record_timestamps
  65. model_metadata.timestamp_column_names.each do |c_name|
  66. @row[c_name] = @now unless @row.key?(c_name)
  67. end
  68. end
  69. end
  70. 1 def interpolate_label
  71. # interpolate the fixture label
  72. @row.each do |key, value|
  73. @row[key] = value.gsub("$LABEL", @label.to_s) if value.is_a?(String)
  74. end
  75. end
  76. 1 def generate_primary_key
  77. # generate a primary key if necessary
  78. if model_metadata.has_primary_key_column? && !@row.include?(model_metadata.primary_key_name)
  79. @row[model_metadata.primary_key_name] = ActiveRecord::FixtureSet.identify(
  80. @label, model_metadata.primary_key_type
  81. )
  82. end
  83. end
  84. 1 def resolve_enums
  85. model_class.defined_enums.each do |name, values|
  86. if @row.include?(name)
  87. @row[name] = values.fetch(@row[name], @row[name])
  88. end
  89. end
  90. end
  91. 1 def resolve_sti_reflections
  92. # If STI is used, find the correct subclass for association reflection
  93. reflection_class._reflections.each_value do |association|
  94. case association.macro
  95. when :belongs_to
  96. # Do not replace association name with association foreign key if they are named the same
  97. fk_name = association.join_foreign_key
  98. if association.name.to_s != fk_name && value = @row.delete(association.name.to_s)
  99. if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
  100. # support polymorphic belongs_to as "label (Type)"
  101. @row[association.join_foreign_type] = $1
  102. end
  103. fk_type = reflection_class.type_for_attribute(fk_name).type
  104. @row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
  105. end
  106. when :has_many
  107. if association.options[:through]
  108. add_join_records(HasManyThroughProxy.new(association))
  109. end
  110. end
  111. end
  112. end
  113. 1 def add_join_records(association)
  114. # This is the case when the join table has no fixtures file
  115. if (targets = @row.delete(association.name.to_s))
  116. table_name = association.join_table
  117. column_type = association.primary_key_type
  118. lhs_key = association.lhs_key
  119. rhs_key = association.rhs_key
  120. targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
  121. joins = targets.map do |target|
  122. { lhs_key => @row[model_metadata.primary_key_name],
  123. rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
  124. end
  125. @table_rows.tables[table_name].concat(joins)
  126. end
  127. end
  128. end
  129. end
  130. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixture_set/table_rows.rb

55.0% lines covered

20 relevant lines. 11 lines covered and 9 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_record/fixture_set/table_row"
  3. 1 require "active_record/fixture_set/model_metadata"
  4. 1 module ActiveRecord
  5. 1 class FixtureSet
  6. 1 class TableRows # :nodoc:
  7. 1 def initialize(table_name, model_class:, fixtures:, config:)
  8. @model_class = model_class
  9. # track any join tables we need to insert later
  10. @tables = Hash.new { |h, table| h[table] = [] }
  11. # ensure this table is loaded before any HABTM associations
  12. @tables[table_name] = nil
  13. build_table_rows_from(table_name, fixtures, config)
  14. end
  15. 1 attr_reader :tables, :model_class
  16. 1 def to_hash
  17. @tables.transform_values { |rows| rows.map(&:to_hash) }
  18. end
  19. 1 def model_metadata
  20. @model_metadata ||= ModelMetadata.new(model_class)
  21. end
  22. 1 private
  23. 1 def build_table_rows_from(table_name, fixtures, config)
  24. now = config.default_timezone == :utc ? Time.now.utc : Time.now
  25. @tables[table_name] = fixtures.map do |label, fixture|
  26. TableRow.new(
  27. fixture,
  28. table_rows: self,
  29. label: label,
  30. now: now,
  31. )
  32. end
  33. end
  34. end
  35. end
  36. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/fixtures.rb

44.91% lines covered

167 relevant lines. 75 lines covered and 92 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "erb"
  3. 1 require "yaml"
  4. 1 require "zlib"
  5. 1 require "set"
  6. 1 require "active_support/dependencies"
  7. 1 require "active_support/core_ext/digest/uuid"
  8. 1 require "active_record/fixture_set/file"
  9. 1 require "active_record/fixture_set/render_context"
  10. 1 require "active_record/fixture_set/table_rows"
  11. 1 require "active_record/test_fixtures"
  12. 1 module ActiveRecord
  13. 1 class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
  14. end
  15. # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
  16. #
  17. # They are stored in YAML files, one file per model, which are placed in the directory
  18. # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
  19. # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
  20. # The fixture file ends with the +.yml+ file extension, for example:
  21. # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
  22. #
  23. # The format of a fixture file looks like this:
  24. #
  25. # rubyonrails:
  26. # id: 1
  27. # name: Ruby on Rails
  28. # url: http://www.rubyonrails.org
  29. #
  30. # google:
  31. # id: 2
  32. # name: Google
  33. # url: http://www.google.com
  34. #
  35. # This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
  36. # is followed by an indented list of key/value pairs in the "key: value" format. Records are
  37. # separated by a blank line for your viewing pleasure.
  38. #
  39. # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
  40. # See https://yaml.org/type/omap.html
  41. # for the specification. You will need ordered fixtures when you have foreign key constraints
  42. # on keys in the same table. This is commonly needed for tree structures. Example:
  43. #
  44. # --- !omap
  45. # - parent:
  46. # id: 1
  47. # parent_id: NULL
  48. # title: Parent
  49. # - child:
  50. # id: 2
  51. # parent_id: 1
  52. # title: Child
  53. #
  54. # = Using Fixtures in Test Cases
  55. #
  56. # Since fixtures are a testing construct, we use them in our unit and functional tests. There
  57. # are two ways to use the fixtures, but first let's take a look at a sample unit test:
  58. #
  59. # require "test_helper"
  60. #
  61. # class WebSiteTest < ActiveSupport::TestCase
  62. # test "web_site_count" do
  63. # assert_equal 2, WebSite.count
  64. # end
  65. # end
  66. #
  67. # By default, +test_helper.rb+ will load all of your fixtures into your test
  68. # database, so this test will succeed.
  69. #
  70. # The testing environment will automatically load all the fixtures into the database before each
  71. # test. To ensure consistent data, the environment deletes the fixtures before running the load.
  72. #
  73. # In addition to being available in the database, the fixture's data may also be accessed by
  74. # using a special dynamic method, which has the same name as the model.
  75. #
  76. # Passing in a fixture name to this dynamic method returns the fixture matching this name:
  77. #
  78. # test "find one" do
  79. # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
  80. # end
  81. #
  82. # Passing in multiple fixture names returns all fixtures matching these names:
  83. #
  84. # test "find all by name" do
  85. # assert_equal 2, web_sites(:rubyonrails, :google).length
  86. # end
  87. #
  88. # Passing in no arguments returns all fixtures:
  89. #
  90. # test "find all" do
  91. # assert_equal 2, web_sites.length
  92. # end
  93. #
  94. # Passing in any fixture name that does not exist will raise <tt>StandardError</tt>:
  95. #
  96. # test "find by name that does not exist" do
  97. # assert_raise(StandardError) { web_sites(:reddit) }
  98. # end
  99. #
  100. # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
  101. # following tests:
  102. #
  103. # test "find_alt_method_1" do
  104. # assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
  105. # end
  106. #
  107. # test "find_alt_method_2" do
  108. # assert_equal "Ruby on Rails", @rubyonrails.name
  109. # end
  110. #
  111. # In order to use these methods to access fixtured data within your test cases, you must specify one of the
  112. # following in your ActiveSupport::TestCase-derived class:
  113. #
  114. # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
  115. # self.use_instantiated_fixtures = true
  116. #
  117. # - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
  118. # self.use_instantiated_fixtures = :no_instances
  119. #
  120. # Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
  121. # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
  122. # large sets of fixtured data.
  123. #
  124. # = Dynamic fixtures with ERB
  125. #
  126. # Sometimes you don't care about the content of the fixtures as much as you care about the volume.
  127. # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
  128. # testing, like:
  129. #
  130. # <% 1.upto(1000) do |i| %>
  131. # fix_<%= i %>:
  132. # id: <%= i %>
  133. # name: guy_<%= i %>
  134. # <% end %>
  135. #
  136. # This will create 1000 very simple fixtures.
  137. #
  138. # Using ERB, you can also inject dynamic values into your fixtures with inserts like
  139. # <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
  140. # This is however a feature to be used with some caution. The point of fixtures are that they're
  141. # stable units of predictable sample data. If you feel that you need to inject dynamic values, then
  142. # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
  143. # in fixtures are to be considered a code smell.
  144. #
  145. # Helper methods defined in a fixture will not be available in other fixtures, to prevent against
  146. # unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
  147. # that is included in ActiveRecord::FixtureSet.context_class.
  148. #
  149. # - define a helper method in <tt>test_helper.rb</tt>
  150. # module FixtureFileHelpers
  151. # def file_sha(path)
  152. # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
  153. # end
  154. # end
  155. # ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
  156. #
  157. # - use the helper method in a fixture
  158. # photo:
  159. # name: kitten.png
  160. # sha: <%= file_sha 'files/kitten.png' %>
  161. #
  162. # = Transactional Tests
  163. #
  164. # Test cases can use begin+rollback to isolate their changes to the database instead of having to
  165. # delete+insert for every test case.
  166. #
  167. # class FooTest < ActiveSupport::TestCase
  168. # self.use_transactional_tests = true
  169. #
  170. # test "godzilla" do
  171. # assert_not_empty Foo.all
  172. # Foo.destroy_all
  173. # assert_empty Foo.all
  174. # end
  175. #
  176. # test "godzilla aftermath" do
  177. # assert_not_empty Foo.all
  178. # end
  179. # end
  180. #
  181. # If you preload your test database with all fixture data (probably by running <tt>bin/rails db:fixtures:load</tt>)
  182. # and use transactional tests, then you may omit all fixtures declarations in your test cases since
  183. # all the data's already there and every case rolls back its changes.
  184. #
  185. # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
  186. # true. This will provide access to fixture data for every table that has been loaded through
  187. # fixtures (depending on the value of +use_instantiated_fixtures+).
  188. #
  189. # When *not* to use transactional tests:
  190. #
  191. # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
  192. # all parent transactions commit, particularly, the fixtures transaction which is begun in setup
  193. # and rolled back in teardown. Thus, you won't be able to verify
  194. # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
  195. # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
  196. # Use InnoDB, MaxDB, or NDB instead.
  197. #
  198. # = Advanced Fixtures
  199. #
  200. # Fixtures that don't specify an ID get some extra features:
  201. #
  202. # * Stable, autogenerated IDs
  203. # * Label references for associations (belongs_to, has_one, has_many)
  204. # * HABTM associations as inline lists
  205. #
  206. # There are some more advanced features available even if the id is specified:
  207. #
  208. # * Autofilled timestamp columns
  209. # * Fixture label interpolation
  210. # * Support for YAML defaults
  211. #
  212. # == Stable, Autogenerated IDs
  213. #
  214. # Here, have a monkey fixture:
  215. #
  216. # george:
  217. # id: 1
  218. # name: George the Monkey
  219. #
  220. # reginald:
  221. # id: 2
  222. # name: Reginald the Pirate
  223. #
  224. # Each of these fixtures has two unique identifiers: one for the database
  225. # and one for the humans. Why don't we generate the primary key instead?
  226. # Hashing each fixture's label yields a consistent ID:
  227. #
  228. # george: # generated id: 503576764
  229. # name: George the Monkey
  230. #
  231. # reginald: # generated id: 324201669
  232. # name: Reginald the Pirate
  233. #
  234. # Active Record looks at the fixture's model class, discovers the correct
  235. # primary key, and generates it right before inserting the fixture
  236. # into the database.
  237. #
  238. # The generated ID for a given label is constant, so we can discover
  239. # any fixture's ID without loading anything, as long as we know the label.
  240. #
  241. # == Label references for associations (belongs_to, has_one, has_many)
  242. #
  243. # Specifying foreign keys in fixtures can be very fragile, not to
  244. # mention difficult to read. Since Active Record can figure out the ID of
  245. # any fixture from its label, you can specify FK's by label instead of ID.
  246. #
  247. # === belongs_to
  248. #
  249. # Let's break out some more monkeys and pirates.
  250. #
  251. # ### in pirates.yml
  252. #
  253. # reginald:
  254. # id: 1
  255. # name: Reginald the Pirate
  256. # monkey_id: 1
  257. #
  258. # ### in monkeys.yml
  259. #
  260. # george:
  261. # id: 1
  262. # name: George the Monkey
  263. # pirate_id: 1
  264. #
  265. # Add a few more monkeys and pirates and break this into multiple files,
  266. # and it gets pretty hard to keep track of what's going on. Let's
  267. # use labels instead of IDs:
  268. #
  269. # ### in pirates.yml
  270. #
  271. # reginald:
  272. # name: Reginald the Pirate
  273. # monkey: george
  274. #
  275. # ### in monkeys.yml
  276. #
  277. # george:
  278. # name: George the Monkey
  279. # pirate: reginald
  280. #
  281. # Pow! All is made clear. Active Record reflects on the fixture's model class,
  282. # finds all the +belongs_to+ associations, and allows you to specify
  283. # a target *label* for the *association* (monkey: george) rather than
  284. # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
  285. #
  286. # ==== Polymorphic belongs_to
  287. #
  288. # Supporting polymorphic relationships is a little bit more complicated, since
  289. # Active Record needs to know what type your association is pointing at. Something
  290. # like this should look familiar:
  291. #
  292. # ### in fruit.rb
  293. #
  294. # belongs_to :eater, polymorphic: true
  295. #
  296. # ### in fruits.yml
  297. #
  298. # apple:
  299. # id: 1
  300. # name: apple
  301. # eater_id: 1
  302. # eater_type: Monkey
  303. #
  304. # Can we do better? You bet!
  305. #
  306. # apple:
  307. # eater: george (Monkey)
  308. #
  309. # Just provide the polymorphic target type and Active Record will take care of the rest.
  310. #
  311. # === has_and_belongs_to_many
  312. #
  313. # Time to give our monkey some fruit.
  314. #
  315. # ### in monkeys.yml
  316. #
  317. # george:
  318. # id: 1
  319. # name: George the Monkey
  320. #
  321. # ### in fruits.yml
  322. #
  323. # apple:
  324. # id: 1
  325. # name: apple
  326. #
  327. # orange:
  328. # id: 2
  329. # name: orange
  330. #
  331. # grape:
  332. # id: 3
  333. # name: grape
  334. #
  335. # ### in fruits_monkeys.yml
  336. #
  337. # apple_george:
  338. # fruit_id: 1
  339. # monkey_id: 1
  340. #
  341. # orange_george:
  342. # fruit_id: 2
  343. # monkey_id: 1
  344. #
  345. # grape_george:
  346. # fruit_id: 3
  347. # monkey_id: 1
  348. #
  349. # Let's make the HABTM fixture go away.
  350. #
  351. # ### in monkeys.yml
  352. #
  353. # george:
  354. # id: 1
  355. # name: George the Monkey
  356. # fruits: apple, orange, grape
  357. #
  358. # ### in fruits.yml
  359. #
  360. # apple:
  361. # name: apple
  362. #
  363. # orange:
  364. # name: orange
  365. #
  366. # grape:
  367. # name: grape
  368. #
  369. # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
  370. # on George's fixture, but we could've just as easily specified a list
  371. # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
  372. # the fixture's model class and discovers the +has_and_belongs_to_many+
  373. # associations.
  374. #
  375. # == Autofilled Timestamp Columns
  376. #
  377. # If your table/model specifies any of Active Record's
  378. # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
  379. # they will automatically be set to <tt>Time.now</tt>.
  380. #
  381. # If you've set specific values, they'll be left alone.
  382. #
  383. # == Fixture label interpolation
  384. #
  385. # The label of the current fixture is always available as a column value:
  386. #
  387. # geeksomnia:
  388. # name: Geeksomnia's Account
  389. # subdomain: $LABEL
  390. # email: $LABEL@email.com
  391. #
  392. # Also, sometimes (like when porting older join table fixtures) you'll need
  393. # to be able to get a hold of the identifier for a given label. ERB
  394. # to the rescue:
  395. #
  396. # george_reginald:
  397. # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
  398. # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
  399. #
  400. # == Support for YAML defaults
  401. #
  402. # You can set and reuse defaults in your fixtures YAML file.
  403. # This is the same technique used in the +database.yml+ file to specify
  404. # defaults:
  405. #
  406. # DEFAULTS: &DEFAULTS
  407. # created_on: <%= 3.weeks.ago.to_s(:db) %>
  408. #
  409. # first:
  410. # name: Smurf
  411. # <<: *DEFAULTS
  412. #
  413. # second:
  414. # name: Fraggle
  415. # <<: *DEFAULTS
  416. #
  417. # Any fixture labeled "DEFAULTS" is safely ignored.
  418. #
  419. # Besides using "DEFAULTS", you can also specify what fixtures will
  420. # be ignored by setting "ignore" in "_fixture" section.
  421. #
  422. # # users.yml
  423. # _fixture:
  424. # ignore:
  425. # - base
  426. # # or use "ignore: base" when there is only one fixture needs to be ignored.
  427. #
  428. # base: &base
  429. # admin: false
  430. # introduction: "This is a default description"
  431. #
  432. # admin:
  433. # <<: *base
  434. # admin: true
  435. #
  436. # visitor:
  437. # <<: *base
  438. #
  439. # In the above example, 'base' will be ignored when creating fixtures.
  440. # This can be used for common attributes inheriting.
  441. #
  442. # == Configure the fixture model class
  443. #
  444. # It's possible to set the fixture's model class directly in the YAML file.
  445. # This is helpful when fixtures are loaded outside tests and
  446. # +set_fixture_class+ is not available (e.g.
  447. # when running <tt>bin/rails db:fixtures:load</tt>).
  448. #
  449. # _fixture:
  450. # model_class: User
  451. # david:
  452. # name: David
  453. #
  454. # Any fixtures labeled "_fixture" are safely ignored.
  455. 1 class FixtureSet
  456. #--
  457. # An instance of FixtureSet is normally stored in a single YAML file and
  458. # possibly in a folder with the same name.
  459. #++
  460. 1 MAX_ID = 2**30 - 1
  461. 2 @@all_cached_fixtures = Hash.new { |h, k| h[k] = {} }
  462. 1 cattr_accessor :all_loaded_fixtures, default: {}
  463. 1 class ClassCache
  464. 1 def initialize(class_names, config)
  465. 10 @class_names = class_names.stringify_keys
  466. 10 @config = config
  467. # Remove string values that aren't constants or subclasses of AR
  468. 10 @class_names.delete_if do |klass_name, klass|
  469. !insert_class(@class_names, klass_name, klass)
  470. end
  471. end
  472. 1 def [](fs_name)
  473. @class_names.fetch(fs_name) do
  474. klass = default_fixture_model(fs_name, @config).safe_constantize
  475. insert_class(@class_names, fs_name, klass)
  476. end
  477. end
  478. 1 private
  479. 1 def insert_class(class_names, name, klass)
  480. # We only want to deal with AR objects.
  481. if klass && klass < ActiveRecord::Base
  482. class_names[name] = klass
  483. else
  484. class_names[name] = nil
  485. end
  486. end
  487. 1 def default_fixture_model(fs_name, config)
  488. ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
  489. end
  490. end
  491. 1 class << self
  492. 1 def default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
  493. config.pluralize_table_names ?
  494. fixture_set_name.singularize.camelize :
  495. fixture_set_name.camelize
  496. end
  497. 1 def default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
  498. "#{ config.table_name_prefix }"\
  499. "#{ fixture_set_name.tr('/', '_') }"\
  500. "#{ config.table_name_suffix }".to_sym
  501. end
  502. 1 def reset_cache
  503. @@all_cached_fixtures.clear
  504. end
  505. 1 def cache_for_connection(connection)
  506. 10 @@all_cached_fixtures[connection]
  507. end
  508. 1 def fixture_is_cached?(connection, table_name)
  509. cache_for_connection(connection)[table_name]
  510. end
  511. 1 def cached_fixtures(connection, keys_to_fetch = nil)
  512. 10 if keys_to_fetch
  513. 10 cache_for_connection(connection).values_at(*keys_to_fetch)
  514. else
  515. cache_for_connection(connection).values
  516. end
  517. end
  518. 1 def cache_fixtures(connection, fixtures_map)
  519. cache_for_connection(connection).update(fixtures_map)
  520. end
  521. 1 def instantiate_fixtures(object, fixture_set, load_instances = true)
  522. return unless load_instances
  523. fixture_set.each do |fixture_name, fixture|
  524. object.instance_variable_set "@#{fixture_name}", fixture.find
  525. rescue FixtureClassNotFound
  526. nil
  527. end
  528. end
  529. 1 def instantiate_all_loaded_fixtures(object, load_instances = true)
  530. all_loaded_fixtures.each_value do |fixture_set|
  531. instantiate_fixtures(object, fixture_set, load_instances)
  532. end
  533. end
  534. 1 def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
  535. 10 fixture_set_names = Array(fixture_set_names).map(&:to_s)
  536. 10 class_names = ClassCache.new class_names, config
  537. # FIXME: Apparently JK uses this.
  538. 20 connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
  539. 10 fixture_files_to_read = fixture_set_names.reject do |fs_name|
  540. fixture_is_cached?(connection.call, fs_name)
  541. end
  542. 10 if fixture_files_to_read.any?
  543. fixtures_map = read_and_insert(
  544. fixtures_directory,
  545. fixture_files_to_read,
  546. class_names,
  547. connection,
  548. )
  549. cache_fixtures(connection.call, fixtures_map)
  550. end
  551. 10 cached_fixtures(connection.call, fixture_set_names)
  552. end
  553. # Returns a consistent, platform-independent identifier for +label+.
  554. # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
  555. 1 def identify(label, column_type = :integer)
  556. if column_type == :uuid
  557. Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
  558. else
  559. Zlib.crc32(label.to_s) % MAX_ID
  560. end
  561. end
  562. 1 def signed_global_id(fixture_set_name, label, column_type: :integer, **options)
  563. identifier = identify(label, column_type)
  564. model_name = default_fixture_model_name(fixture_set_name)
  565. uri = URI::GID.build([GlobalID.app, model_name, identifier, {}])
  566. SignedGlobalID.new(uri, **options)
  567. end
  568. # Superclass for the evaluation contexts used by ERB fixtures.
  569. 1 def context_class
  570. @context_class ||= Class.new
  571. end
  572. 1 private
  573. 1 def read_and_insert(fixtures_directory, fixture_files, class_names, connection) # :nodoc:
  574. fixtures_map = {}
  575. fixture_sets = fixture_files.map do |fixture_set_name|
  576. klass = class_names[fixture_set_name]
  577. fixtures_map[fixture_set_name] = new( # ActiveRecord::FixtureSet.new
  578. nil,
  579. fixture_set_name,
  580. klass,
  581. ::File.join(fixtures_directory, fixture_set_name)
  582. )
  583. end
  584. update_all_loaded_fixtures(fixtures_map)
  585. insert(fixture_sets, connection)
  586. fixtures_map
  587. end
  588. 1 def insert(fixture_sets, connection) # :nodoc:
  589. fixture_sets_by_connection = fixture_sets.group_by do |fixture_set|
  590. if fixture_set.model_class
  591. fixture_set.model_class.connection
  592. else
  593. connection.call
  594. end
  595. end
  596. fixture_sets_by_connection.each do |conn, set|
  597. table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
  598. set.each do |fixture_set|
  599. fixture_set.table_rows.each do |table, rows|
  600. table_rows_for_connection[table].unshift(*rows)
  601. end
  602. end
  603. conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
  604. # Cap primary key sequences to max(pk).
  605. if conn.respond_to?(:reset_pk_sequence!)
  606. set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
  607. end
  608. end
  609. end
  610. 1 def update_all_loaded_fixtures(fixtures_map) # :nodoc:
  611. all_loaded_fixtures.update(fixtures_map)
  612. end
  613. end
  614. 1 attr_reader :table_name, :name, :fixtures, :model_class, :ignored_fixtures, :config
  615. 1 def initialize(_, name, class_name, path, config = ActiveRecord::Base)
  616. @name = name
  617. @path = path
  618. @config = config
  619. self.model_class = class_name
  620. @fixtures = read_fixture_files(path)
  621. @table_name = model_class&.table_name || self.class.default_fixture_table_name(name, config)
  622. end
  623. 1 def [](x)
  624. fixtures[x]
  625. end
  626. 1 def []=(k, v)
  627. fixtures[k] = v
  628. end
  629. 1 def each(&block)
  630. fixtures.each(&block)
  631. end
  632. 1 def size
  633. fixtures.size
  634. end
  635. # Returns a hash of rows to be inserted. The key is the table, the value is
  636. # a list of rows to insert to that table.
  637. 1 def table_rows
  638. # allow specifying fixtures to be ignored by setting `ignore` in `_fixture` section
  639. fixtures.except!(*ignored_fixtures)
  640. TableRows.new(
  641. table_name,
  642. model_class: model_class,
  643. fixtures: fixtures,
  644. config: config,
  645. ).to_hash
  646. end
  647. 1 private
  648. 1 def model_class=(class_name)
  649. if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
  650. @model_class = class_name
  651. else
  652. @model_class = class_name.safe_constantize if class_name
  653. end
  654. end
  655. 1 def ignored_fixtures=(base)
  656. @ignored_fixtures =
  657. case base
  658. when Array
  659. base
  660. when String
  661. [base]
  662. else
  663. []
  664. end
  665. @ignored_fixtures << "DEFAULTS" unless @ignored_fixtures.include?("DEFAULTS")
  666. @ignored_fixtures.compact
  667. end
  668. # Loads the fixtures from the YAML file at +path+.
  669. # If the file sets the +model_class+ and current instance value is not set,
  670. # it uses the file value.
  671. 1 def read_fixture_files(path)
  672. yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
  673. ::File.file?(f)
  674. } + [yaml_file_path(path)]
  675. yaml_files.each_with_object({}) do |file, fixtures|
  676. FixtureSet::File.open(file) do |fh|
  677. self.model_class ||= fh.model_class if fh.model_class
  678. self.ignored_fixtures ||= fh.ignored_fixtures
  679. fh.each do |fixture_name, row|
  680. fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
  681. end
  682. end
  683. end
  684. end
  685. 1 def yaml_file_path(path)
  686. "#{path}.yml"
  687. end
  688. end
  689. 1 class Fixture #:nodoc:
  690. 1 include Enumerable
  691. 1 class FixtureError < StandardError #:nodoc:
  692. end
  693. 1 class FormatError < FixtureError #:nodoc:
  694. end
  695. 1 attr_reader :model_class, :fixture
  696. 1 def initialize(fixture, model_class)
  697. @fixture = fixture
  698. @model_class = model_class
  699. end
  700. 1 def class_name
  701. model_class.name if model_class
  702. end
  703. 1 def each
  704. fixture.each { |item| yield item }
  705. end
  706. 1 def [](key)
  707. fixture[key]
  708. end
  709. 1 alias :to_hash :fixture
  710. 1 def find
  711. raise FixtureClassNotFound, "No class attached to find." unless model_class
  712. object = model_class.unscoped do
  713. model_class.find(fixture[model_class.primary_key])
  714. end
  715. # Fixtures can't be eagerly loaded
  716. object.instance_variable_set(:@strict_loading, false)
  717. object
  718. end
  719. end
  720. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/internal_metadata.rb

51.72% lines covered

29 relevant lines. 15 lines covered and 14 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_record/scoping/default"
  3. 1 require "active_record/scoping/named"
  4. 1 module ActiveRecord
  5. # This class is used to create a table that keeps track of values and keys such
  6. # as which environment migrations were run in.
  7. #
  8. # This is enabled by default. To disable this functionality set
  9. # `use_metadata_table` to false in your database configuration.
  10. 1 class InternalMetadata < ActiveRecord::Base # :nodoc:
  11. 1 class << self
  12. 1 def enabled?
  13. 1 ActiveRecord::Base.connection.use_metadata_table?
  14. end
  15. 1 def _internal?
  16. true
  17. end
  18. 1 def primary_key
  19. "key"
  20. end
  21. 1 def table_name
  22. 1 "#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
  23. end
  24. 1 def []=(key, value)
  25. return unless enabled?
  26. find_or_initialize_by(key: key).update!(value: value)
  27. end
  28. 1 def [](key)
  29. return unless enabled?
  30. where(key: key).pluck(:value).first
  31. end
  32. # Creates an internal metadata table with columns +key+ and +value+
  33. 1 def create_table
  34. return unless enabled?
  35. unless connection.table_exists?(table_name)
  36. connection.create_table(table_name, id: false) do |t|
  37. t.string :key, **connection.internal_string_options_for_primary_key
  38. t.string :value
  39. t.timestamps
  40. end
  41. end
  42. end
  43. 1 def drop_table
  44. return unless enabled?
  45. connection.drop_table table_name, if_exists: true
  46. end
  47. end
  48. end
  49. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/null_relation.rb

51.52% lines covered

33 relevant lines. 17 lines covered and 16 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 module NullRelation # :nodoc:
  4. 1 def pluck(*column_names)
  5. []
  6. end
  7. 1 def delete_all
  8. 0
  9. end
  10. 1 def update_all(_updates)
  11. 0
  12. end
  13. 1 def delete(_id_or_array)
  14. 0
  15. end
  16. 1 def empty?
  17. true
  18. end
  19. 1 def none?
  20. true
  21. end
  22. 1 def any?
  23. false
  24. end
  25. 1 def one?
  26. false
  27. end
  28. 1 def many?
  29. false
  30. end
  31. 1 def to_sql
  32. ""
  33. end
  34. 1 def calculate(operation, _column_name)
  35. case operation
  36. when :count, :sum
  37. group_values.any? ? Hash.new : 0
  38. when :average, :minimum, :maximum
  39. group_values.any? ? Hash.new : nil
  40. end
  41. end
  42. 1 def exists?(_conditions = :none)
  43. false
  44. end
  45. 1 def or(other)
  46. other.spawn
  47. end
  48. 1 private
  49. 1 def exec_queries
  50. @records = [].freeze
  51. end
  52. end
  53. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder.rb

62.22% lines covered

90 relevant lines. 56 lines covered and 34 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class PredicateBuilder # :nodoc:
  4. 1 require "active_record/relation/predicate_builder/array_handler"
  5. 1 require "active_record/relation/predicate_builder/basic_object_handler"
  6. 1 require "active_record/relation/predicate_builder/range_handler"
  7. 1 require "active_record/relation/predicate_builder/relation_handler"
  8. 1 require "active_record/relation/predicate_builder/association_query_value"
  9. 1 require "active_record/relation/predicate_builder/polymorphic_array_value"
  10. # No-op BaseHandler to work Mashal.load(File.read("legacy_relation.dump")).
  11. # TODO: Remove the constant alias once Rails 6.1 has released.
  12. 1 BaseHandler = BasicObjectHandler
  13. 1 def initialize(table)
  14. 9 @table = table
  15. 9 @handlers = []
  16. 9 register_handler(BasicObject, BasicObjectHandler.new(self))
  17. 9 register_handler(Range, RangeHandler.new(self))
  18. 9 register_handler(Relation, RelationHandler.new)
  19. 9 register_handler(Array, ArrayHandler.new(self))
  20. 9 register_handler(Set, ArrayHandler.new(self))
  21. end
  22. 1 def build_from_hash(attributes, &block)
  23. 17 attributes = convert_dot_notation_to_hash(attributes)
  24. 17 expand_from_hash(attributes, &block)
  25. end
  26. 1 def self.references(attributes)
  27. 17 attributes.each_with_object([]) do |(key, value), result|
  28. 17 if value.is_a?(Hash)
  29. 1 result << Arel.sql(key)
  30. 16 elsif key.include?(".")
  31. result << Arel.sql(key.split(".").first)
  32. end
  33. end
  34. end
  35. # Define how a class is converted to Arel nodes when passed to +where+.
  36. # The handler can be any object that responds to +call+, and will be used
  37. # for any value that +===+ the class given. For example:
  38. #
  39. # MyCustomDateRange = Struct.new(:start, :end)
  40. # handler = proc do |column, range|
  41. # Arel::Nodes::Between.new(column,
  42. # Arel::Nodes::And.new([range.start, range.end])
  43. # )
  44. # end
  45. # ActiveRecord::PredicateBuilder.new("users").register_handler(MyCustomDateRange, handler)
  46. 1 def register_handler(klass, handler)
  47. 45 @handlers.unshift([klass, handler])
  48. end
  49. 1 def [](attr_name, value, operator = nil)
  50. 17 build(table.arel_table[attr_name], value, operator)
  51. end
  52. 1 def build(attribute, value, operator = nil)
  53. 17 value = value.id if value.respond_to?(:id)
  54. 17 if operator ||= table.type(attribute.name).force_equality?(value) && :eq
  55. bind = build_bind_attribute(attribute.name, value)
  56. attribute.public_send(operator, bind)
  57. else
  58. 17 handler_for(value).call(attribute, value)
  59. end
  60. end
  61. 1 def build_bind_attribute(column_name, value)
  62. 344 attr = Relation::QueryAttribute.new(column_name, value, table.type(column_name))
  63. 344 Arel::Nodes::BindParam.new(attr)
  64. end
  65. 1 def resolve_arel_attribute(table_name, column_name, &block)
  66. table.associated_table(table_name, &block).arel_table[column_name]
  67. end
  68. 1 protected
  69. 1 def expand_from_hash(attributes, &block)
  70. 18 return ["1=0"] if attributes.empty?
  71. 18 attributes.flat_map do |key, value|
  72. 18 if value.is_a?(Hash) && !table.has_column?(key)
  73. 1 table.associated_table(key, &block)
  74. .predicate_builder.expand_from_hash(value.stringify_keys)
  75. 17 elsif table.associated_with?(key)
  76. # Find the foreign key when using queries such as:
  77. # Post.where(author: author)
  78. #
  79. # For polymorphic relationships, find the foreign key and type:
  80. # PriceEstimate.where(estimate_of: treasure)
  81. associated_table = table.associated_table(key)
  82. if associated_table.polymorphic_association?
  83. value = [value] unless value.is_a?(Array)
  84. klass = PolymorphicArrayValue
  85. elsif associated_table.through_association?
  86. next associated_table.predicate_builder.expand_from_hash(
  87. associated_table.primary_key => value
  88. )
  89. end
  90. klass ||= AssociationQueryValue
  91. queries = klass.new(associated_table, value).queries.map! do |query|
  92. # If the query produced is identical to attributes don't go any deeper.
  93. # Prevents stack level too deep errors when association and foreign_key are identical.
  94. query == attributes ? self[key, value] : expand_from_hash(query)
  95. end
  96. grouping_queries(queries)
  97. 17 elsif table.aggregated_with?(key)
  98. mapping = table.reflect_on_aggregation(key).mapping
  99. values = value.nil? ? [nil] : Array.wrap(value)
  100. if mapping.length == 1 || values.empty?
  101. column_name, aggr_attr = mapping.first
  102. values = values.map do |object|
  103. object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object
  104. end
  105. self[column_name, values]
  106. else
  107. queries = values.map do |object|
  108. mapping.map do |field_attr, aggregate_attr|
  109. self[field_attr, object.try!(aggregate_attr)]
  110. end
  111. end
  112. grouping_queries(queries)
  113. end
  114. else
  115. 17 self[key, value]
  116. end
  117. end
  118. end
  119. 1 private
  120. 1 attr_reader :table
  121. 1 def grouping_queries(queries)
  122. if queries.one?
  123. queries.first
  124. else
  125. queries.map! { |query| query.reduce(&:and) }
  126. queries = queries.reduce { |result, query| Arel::Nodes::Or.new(result, query) }
  127. Arel::Nodes::Grouping.new(queries)
  128. end
  129. end
  130. 1 def convert_dot_notation_to_hash(attributes)
  131. 17 dot_notation = attributes.select do |k, v|
  132. 17 k.include?(".") && !v.is_a?(Hash)
  133. end
  134. 17 dot_notation.each_key do |key|
  135. table_name, column_name = key.split(".")
  136. value = attributes.delete(key)
  137. attributes[table_name] ||= {}
  138. attributes[table_name] = attributes[table_name].merge(column_name => value)
  139. end
  140. 17 attributes
  141. end
  142. 1 def handler_for(object)
  143. 101 @handlers.detect { |klass, _| klass === object }.last
  144. end
  145. end
  146. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/array_handler.rb

42.31% lines covered

26 relevant lines. 11 lines covered and 15 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/array/extract"
  3. 1 module ActiveRecord
  4. 1 class PredicateBuilder
  5. 1 class ArrayHandler # :nodoc:
  6. 1 def initialize(predicate_builder)
  7. 18 @predicate_builder = predicate_builder
  8. end
  9. 1 def call(attribute, value)
  10. return attribute.in([]) if value.empty?
  11. values = value.map { |x| x.is_a?(Base) ? x.id : x }
  12. nils = values.extract!(&:nil?)
  13. ranges = values.extract! { |v| v.is_a?(Range) }
  14. values_predicate =
  15. case values.length
  16. when 0 then NullPredicate
  17. when 1 then predicate_builder.build(attribute, values.first)
  18. else Arel::Nodes::HomogeneousIn.new(values, attribute, :in)
  19. end
  20. unless nils.empty?
  21. values_predicate = values_predicate.or(attribute.eq(nil))
  22. end
  23. if ranges.empty?
  24. values_predicate
  25. else
  26. array_predicates = ranges.map! { |range| predicate_builder.build(attribute, range) }
  27. array_predicates.inject(values_predicate, &:or)
  28. end
  29. end
  30. 1 private
  31. 1 attr_reader :predicate_builder
  32. 1 module NullPredicate # :nodoc:
  33. 1 def self.or(other)
  34. other
  35. end
  36. end
  37. end
  38. end
  39. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/association_query_value.rb

47.62% lines covered

21 relevant lines. 10 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class PredicateBuilder
  4. 1 class AssociationQueryValue # :nodoc:
  5. 1 def initialize(associated_table, value)
  6. @associated_table = associated_table
  7. @value = value
  8. end
  9. 1 def queries
  10. [ associated_table.join_foreign_key => ids ]
  11. end
  12. 1 private
  13. 1 attr_reader :associated_table, :value
  14. 1 def ids
  15. case value
  16. when Relation
  17. value.select_values.empty? ? value.select(primary_key) : value
  18. when Array
  19. value.map { |v| convert_to_id(v) }
  20. else
  21. convert_to_id(value)
  22. end
  23. end
  24. 1 def primary_key
  25. associated_table.join_primary_key
  26. end
  27. 1 def convert_to_id(value)
  28. if value.respond_to?(primary_key)
  29. value.public_send(primary_key)
  30. else
  31. value
  32. end
  33. end
  34. end
  35. end
  36. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/basic_object_handler.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class PredicateBuilder
  4. 1 class BasicObjectHandler # :nodoc:
  5. 1 def initialize(predicate_builder)
  6. 9 @predicate_builder = predicate_builder
  7. end
  8. 1 def call(attribute, value)
  9. 16 bind = predicate_builder.build_bind_attribute(attribute.name, value)
  10. 16 attribute.eq(bind)
  11. end
  12. 1 private
  13. 1 attr_reader :predicate_builder
  14. end
  15. end
  16. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb

36.67% lines covered

30 relevant lines. 11 lines covered and 19 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class PredicateBuilder
  4. 1 class PolymorphicArrayValue # :nodoc:
  5. 1 def initialize(associated_table, values)
  6. @associated_table = associated_table
  7. @values = values
  8. end
  9. 1 def queries
  10. return [ associated_table.join_foreign_key => values ] if values.empty?
  11. type_to_ids_mapping.map do |type, ids|
  12. query = {}
  13. query[associated_table.join_foreign_type] = type if type
  14. query[associated_table.join_foreign_key] = ids
  15. query
  16. end
  17. end
  18. 1 private
  19. 1 attr_reader :associated_table, :values
  20. 1 def type_to_ids_mapping
  21. default_hash = Hash.new { |hsh, key| hsh[key] = [] }
  22. values.each_with_object(default_hash) do |value, hash|
  23. hash[klass(value)&.polymorphic_name] << convert_to_id(value)
  24. end
  25. end
  26. 1 def primary_key(value)
  27. associated_table.join_primary_key(klass(value))
  28. end
  29. 1 def klass(value)
  30. case value
  31. when Base
  32. value.class
  33. when Relation
  34. value.klass
  35. end
  36. end
  37. 1 def convert_to_id(value)
  38. case value
  39. when Base
  40. value._read_attribute(primary_key(value))
  41. when Relation
  42. value.select(primary_key(value))
  43. else
  44. value
  45. end
  46. end
  47. end
  48. end
  49. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/range_handler.rb

100.0% lines covered

12 relevant lines. 12 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class PredicateBuilder
  4. 1 class RangeHandler # :nodoc:
  5. 1 RangeWithBinds = Struct.new(:begin, :end, :exclude_end?)
  6. 1 def initialize(predicate_builder)
  7. 9 @predicate_builder = predicate_builder
  8. end
  9. 1 def call(attribute, value)
  10. 1 begin_bind = predicate_builder.build_bind_attribute(attribute.name, value.begin)
  11. 1 end_bind = predicate_builder.build_bind_attribute(attribute.name, value.end)
  12. 1 attribute.between(RangeWithBinds.new(begin_bind, end_bind, value.exclude_end?))
  13. end
  14. 1 private
  15. 1 attr_reader :predicate_builder
  16. end
  17. end
  18. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/relation/predicate_builder/relation_handler.rb

44.44% lines covered

9 relevant lines. 4 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class PredicateBuilder
  4. 1 class RelationHandler # :nodoc:
  5. 1 def call(attribute, value)
  6. if value.eager_loading?
  7. value = value.send(:apply_join_dependency)
  8. end
  9. if value.select_values.empty?
  10. value = value.select(value.table[value.klass.primary_key])
  11. end
  12. attribute.in(value.arel)
  13. end
  14. end
  15. end
  16. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/result.rb

80.88% lines covered

68 relevant lines. 55 lines covered and 13 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. ###
  4. # This class encapsulates a result returned from calling
  5. # {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
  6. # on any database connection adapter. For example:
  7. #
  8. # result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
  9. # result # => #<ActiveRecord::Result:0xdeadbeef>
  10. #
  11. # # Get the column names of the result:
  12. # result.columns
  13. # # => ["id", "title", "body"]
  14. #
  15. # # Get the record values of the result:
  16. # result.rows
  17. # # => [[1, "title_1", "body_1"],
  18. # [2, "title_2", "body_2"],
  19. # ...
  20. # ]
  21. #
  22. # # Get an array of hashes representing the result (column => value):
  23. # result.to_a
  24. # # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
  25. # {"id" => 2, "title" => "title_2", "body" => "body_2"},
  26. # ...
  27. # ]
  28. #
  29. # # ActiveRecord::Result also includes Enumerable.
  30. # result.each do |row|
  31. # puts row['title'] + " " + row['body']
  32. # end
  33. 1 class Result
  34. 1 include Enumerable
  35. 1 attr_reader :columns, :rows, :column_types
  36. 1 def initialize(columns, rows, column_types = {})
  37. 97 @columns = columns
  38. 97 @rows = rows
  39. 97 @hash_rows = nil
  40. 97 @column_types = column_types
  41. end
  42. # Returns true if this result set includes the column named +name+
  43. 1 def includes_column?(name)
  44. 20 @columns.include? name
  45. end
  46. # Returns the number of elements in the rows array.
  47. 1 def length
  48. 20 @rows.length
  49. end
  50. # Calls the given block once for each element in row collection, passing
  51. # row as parameter.
  52. #
  53. # Returns an +Enumerator+ if no block is given.
  54. 1 def each
  55. 20 if block_given?
  56. 38 hash_rows.each { |row| yield row }
  57. else
  58. hash_rows.to_enum { @rows.size }
  59. end
  60. end
  61. 1 alias :map! :map
  62. 1 alias :collect! :map
  63. 1 deprecate "map!": :map
  64. 1 deprecate "collect!": :map
  65. # Returns true if there are no records, otherwise false.
  66. 1 def empty?
  67. rows.empty?
  68. end
  69. # Returns an array of hashes representing each row record.
  70. 1 def to_ary
  71. hash_rows
  72. end
  73. 1 alias :to_a :to_ary
  74. 1 def [](idx)
  75. hash_rows[idx]
  76. end
  77. # Returns the last record from the rows collection.
  78. 1 def last(n = nil)
  79. n ? hash_rows.last(n) : hash_rows.last
  80. end
  81. 1 def cast_values(type_overrides = {}) # :nodoc:
  82. 1 if columns.one?
  83. # Separated to avoid allocating an array per row
  84. 1 type = if type_overrides.is_a?(Array)
  85. 1 type_overrides.first
  86. else
  87. column_type(columns.first, type_overrides)
  88. end
  89. 1 rows.map do |(value)|
  90. 11 type.deserialize(value)
  91. end
  92. else
  93. types = if type_overrides.is_a?(Array)
  94. type_overrides
  95. else
  96. columns.map { |name| column_type(name, type_overrides) }
  97. end
  98. rows.map do |values|
  99. Array.new(values.size) { |i| types[i].deserialize(values[i]) }
  100. end
  101. end
  102. end
  103. 1 def initialize_copy(other)
  104. 1 @columns = columns.dup
  105. 1 @rows = rows.dup
  106. 1 @column_types = column_types.dup
  107. 1 @hash_rows = nil
  108. end
  109. 1 private
  110. 1 def column_type(name, type_overrides = {})
  111. type_overrides.fetch(name) do
  112. column_types.fetch(name, Type.default_value)
  113. end
  114. end
  115. 1 def hash_rows
  116. 20 @hash_rows ||=
  117. begin
  118. # We freeze the strings to prevent them getting duped when
  119. # used as keys in ActiveRecord::Base's @attributes hash
  120. 20 columns = @columns.map(&:-@)
  121. 20 length = columns.length
  122. 20 template = nil
  123. 20 @rows.map { |row|
  124. 18 if template
  125. # We use transform_values to build subsequent rows from the
  126. # hash of the first row. This is faster because we avoid any
  127. # reallocs and in Ruby 2.7+ avoid hashing entirely.
  128. 3 index = -1
  129. 3 template.transform_values do
  130. 27 row[index += 1]
  131. end
  132. else
  133. # In the past we used Hash[columns.zip(row)]
  134. # though elegant, the verbose way is much more efficient
  135. # both time and memory wise cause it avoids a big array allocation
  136. # this method is called a lot and needs to be micro optimised
  137. 15 hash = {}
  138. 15 index = 0
  139. 15 while index < length
  140. 71 hash[columns[index]] = row[index]
  141. 71 index += 1
  142. end
  143. # It's possible to select the same column twice, in which case
  144. # we can't use a template
  145. 15 template = hash if hash.length == length
  146. 15 hash
  147. end
  148. }
  149. end
  150. end
  151. end
  152. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/runtime_registry.rb

100.0% lines covered

8 relevant lines. 8 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/per_thread_registry"
  3. 1 module ActiveRecord
  4. # This is a thread locals registry for Active Record. For example:
  5. #
  6. # ActiveRecord::RuntimeRegistry.connection_handler
  7. #
  8. # returns the connection handler local to the current thread.
  9. #
  10. # See the documentation of ActiveSupport::PerThreadRegistry
  11. # for further details.
  12. 1 class RuntimeRegistry # :nodoc:
  13. 1 extend ActiveSupport::PerThreadRegistry
  14. 1 attr_accessor :sql_runtime
  15. 1 [:sql_runtime].each do |val|
  16. 1 class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
  17. 1 class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
  18. end
  19. end
  20. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/schema_migration.rb

64.0% lines covered

25 relevant lines. 16 lines covered and 9 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_record/scoping/default"
  3. 1 require "active_record/scoping/named"
  4. 1 module ActiveRecord
  5. # This class is used to create a table that keeps track of which migrations
  6. # have been applied to a given database. When a migration is run, its schema
  7. # number is inserted in to the `SchemaMigration.table_name` so it doesn't need
  8. # to be executed the next time.
  9. 1 class SchemaMigration < ActiveRecord::Base # :nodoc:
  10. 1 class << self
  11. 1 def _internal?
  12. true
  13. end
  14. 1 def primary_key
  15. "version"
  16. end
  17. 1 def table_name
  18. 4 "#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
  19. end
  20. 1 def create_table
  21. unless connection.table_exists?(table_name)
  22. connection.create_table(table_name, id: false) do |t|
  23. t.string :version, **connection.internal_string_options_for_primary_key
  24. end
  25. end
  26. end
  27. 1 def drop_table
  28. connection.drop_table table_name, if_exists: true
  29. end
  30. 1 def normalize_migration_number(number)
  31. "%.3d" % number.to_i
  32. end
  33. 1 def normalized_versions
  34. all_versions.map { |v| normalize_migration_number v }
  35. end
  36. 1 def all_versions
  37. 1 order(:version).pluck(:version)
  38. end
  39. end
  40. 1 def version
  41. super.to_i
  42. end
  43. end
  44. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/statement_cache.rb

87.34% lines covered

79 relevant lines. 69 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. # Statement cache is used to cache a single statement in order to avoid creating the AST again.
  4. # Initializing the cache is done by passing the statement in the create block:
  5. #
  6. # cache = StatementCache.create(Book.connection) do |params|
  7. # Book.where(name: "my book").where("author_id > 3")
  8. # end
  9. #
  10. # The cached statement is executed by using the
  11. # {connection.execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute] method:
  12. #
  13. # cache.execute([], Book.connection)
  14. #
  15. # The relation returned by the block is cached, and for each
  16. # {execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute]
  17. # call the cached relation gets duped. Database is queried when +to_a+ is called on the relation.
  18. #
  19. # If you want to cache the statement without the values you can use the +bind+ method of the
  20. # block parameter.
  21. #
  22. # cache = StatementCache.create(Book.connection) do |params|
  23. # Book.where(name: params.bind)
  24. # end
  25. #
  26. # And pass the bind values as the first argument of +execute+ call.
  27. #
  28. # cache.execute(["my book"], Book.connection)
  29. 1 class StatementCache # :nodoc:
  30. 1 class Substitute; end # :nodoc:
  31. 1 class Query # :nodoc:
  32. 1 def initialize(sql)
  33. @sql = sql
  34. end
  35. 1 def sql_for(binds, connection)
  36. @sql
  37. end
  38. end
  39. 1 class PartialQuery < Query # :nodoc:
  40. 1 def initialize(values)
  41. 4 @values = values
  42. 4 @indexes = values.each_with_index.find_all { |thing, i|
  43. 64 Substitute === thing
  44. }.map(&:last)
  45. end
  46. 1 def sql_for(binds, connection)
  47. 15 val = @values.dup
  48. 15 @indexes.each do |i|
  49. 30 value = binds.shift
  50. 30 if ActiveModel::Attribute === value
  51. 30 value = value.value_for_database
  52. end
  53. 30 val[i] = connection.quote(value)
  54. end
  55. 15 val.join
  56. end
  57. end
  58. 1 class PartialQueryCollector
  59. 1 attr_accessor :preparable
  60. 1 def initialize
  61. 4 @parts = []
  62. 4 @binds = []
  63. end
  64. 1 def <<(str)
  65. 56 @parts << str
  66. 56 self
  67. end
  68. 1 def add_bind(obj)
  69. 8 @binds << obj
  70. 8 @parts << Substitute.new
  71. 8 self
  72. end
  73. 1 def add_binds(binds)
  74. @binds.concat binds
  75. binds.size.times do |i|
  76. @parts << ", " unless i == 0
  77. @parts << Substitute.new
  78. end
  79. self
  80. end
  81. 1 def value
  82. 4 [@parts, @binds]
  83. end
  84. end
  85. 1 def self.query(sql)
  86. Query.new(sql)
  87. end
  88. 1 def self.partial_query(values)
  89. 4 PartialQuery.new(values)
  90. end
  91. 1 def self.partial_query_collector
  92. 4 PartialQueryCollector.new
  93. end
  94. 1 class Params # :nodoc:
  95. 5 def bind; Substitute.new; end
  96. end
  97. 1 class BindMap # :nodoc:
  98. 1 def initialize(bound_attributes)
  99. 4 @indexes = []
  100. 4 @bound_attributes = bound_attributes
  101. 4 bound_attributes.each_with_index do |attr, i|
  102. 8 if ActiveModel::Attribute === attr && Substitute === attr.value
  103. 4 @indexes << i
  104. end
  105. end
  106. end
  107. 1 def bind(values)
  108. 15 bas = @bound_attributes.dup
  109. 30 @indexes.each_with_index { |offset, i| bas[offset] = bas[offset].with_cast_value(values[i]) }
  110. 15 bas
  111. end
  112. end
  113. 1 def self.create(connection, callable = nil, &block)
  114. 4 relation = (callable || block).call Params.new
  115. 4 query_builder, binds = connection.cacheable_query(self, relation.arel)
  116. 4 bind_map = BindMap.new(binds)
  117. 4 new(query_builder, bind_map, relation.klass)
  118. end
  119. 1 def initialize(query_builder, bind_map, klass)
  120. 4 @query_builder = query_builder
  121. 4 @bind_map = bind_map
  122. 4 @klass = klass
  123. end
  124. 1 def execute(params, connection, &block)
  125. 15 bind_values = bind_map.bind params
  126. 15 sql = query_builder.sql_for bind_values, connection
  127. 15 klass.find_by_sql(sql, bind_values, preparable: true, &block)
  128. rescue ::RangeError
  129. []
  130. end
  131. 1 def self.unsupported_value?(value)
  132. 9 case value
  133. when NilClass, Array, Range, Hash, Relation, Base then true
  134. end
  135. end
  136. 1 private
  137. 1 attr_reader :query_builder, :bind_map, :klass
  138. end
  139. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/table_metadata.rb

78.26% lines covered

46 relevant lines. 36 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveRecord
  3. 1 class TableMetadata # :nodoc:
  4. 1 delegate :join_primary_key, :join_foreign_key, :join_foreign_type, to: :reflection
  5. 1 def initialize(klass, arel_table, reflection = nil)
  6. 10 @klass = klass
  7. 10 @arel_table = arel_table
  8. 10 @reflection = reflection
  9. end
  10. 1 def primary_key
  11. klass&.primary_key
  12. end
  13. 1 def type(column_name)
  14. 361 arel_table.type_for_attribute(column_name)
  15. end
  16. 1 def has_column?(column_name)
  17. 1 klass&.columns_hash.key?(column_name)
  18. end
  19. 1 def associated_with?(table_name)
  20. 17 klass&._reflect_on_association(table_name) || klass&._reflect_on_association(table_name.singularize)
  21. end
  22. 1 def associated_table(table_name)
  23. 1 reflection = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.singularize)
  24. 1 if !reflection && table_name == arel_table.name
  25. return self
  26. end
  27. 1 if reflection
  28. 1 association_klass = reflection.klass unless reflection.polymorphic?
  29. elsif block_given?
  30. association_klass = yield table_name
  31. end
  32. 1 if association_klass
  33. 1 arel_table = association_klass.arel_table
  34. 1 arel_table = arel_table.alias(table_name) if arel_table.name != table_name
  35. 1 TableMetadata.new(association_klass, arel_table, reflection)
  36. else
  37. type_caster = TypeCaster::Connection.new(klass, table_name)
  38. arel_table = Arel::Table.new(table_name, type_caster: type_caster)
  39. TableMetadata.new(nil, arel_table, reflection)
  40. end
  41. end
  42. 1 def polymorphic_association?
  43. reflection&.polymorphic?
  44. end
  45. 1 def through_association?
  46. reflection&.through_reflection?
  47. end
  48. 1 def reflect_on_aggregation(aggregation_name)
  49. 17 klass&.reflect_on_aggregation(aggregation_name)
  50. end
  51. 1 alias :aggregated_with? :reflect_on_aggregation
  52. 1 def predicate_builder
  53. 1 if klass
  54. 1 predicate_builder = klass.predicate_builder.dup
  55. 1 predicate_builder.instance_variable_set(:@table, self)
  56. 1 predicate_builder
  57. else
  58. PredicateBuilder.new(self)
  59. end
  60. end
  61. 1 attr_reader :arel_table
  62. 1 private
  63. 1 attr_reader :klass, :reflection
  64. end
  65. end

vender/bundle/ruby/2.6.0/gems/activerecord-6.1.3/lib/active_record/test_fixtures.rb

53.38% lines covered

133 relevant lines. 71 lines covered and 62 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/enumerable"
  3. 1 module ActiveRecord
  4. 1 module TestFixtures
  5. 1 extend ActiveSupport::Concern
  6. 1 def before_setup # :nodoc:
  7. 16 setup_fixtures
  8. 16 super
  9. end
  10. 1 def after_teardown # :nodoc:
  11. 16 super
  12. 16 teardown_fixtures
  13. end
  14. 1 included do
  15. 13 class_attribute :fixture_path, instance_writer: false
  16. 13 class_attribute :fixture_table_names, default: []
  17. 13 class_attribute :fixture_class_names, default: {}
  18. 13 class_attribute :use_transactional_tests, default: true
  19. 13 class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
  20. 13 class_attribute :pre_loaded_fixtures, default: false
  21. 13 class_attribute :lock_threads, default: true
  22. end
  23. 1 module ClassMethods
  24. # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
  25. #
  26. # Examples:
  27. #
  28. # set_fixture_class some_fixture: SomeModel,
  29. # 'namespaced/fixture' => Another::Model
  30. #
  31. # The keys must be the fixture names, that coincide with the short paths to the fixture files.
  32. 1 def set_fixture_class(class_names = {})
  33. self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
  34. end
  35. 1 def fixtures(*fixture_set_names)
  36. if fixture_set_names.first == :all
  37. raise StandardError, "No fixture path found. Please set `#{self}.fixture_path`." if fixture_path.blank?
  38. fixture_set_names = Dir[::File.join(fixture_path, "{**,*}/*.{yml}")].uniq
  39. fixture_set_names.reject! { |f| f.start_with?(file_fixture_path.to_s) } if defined?(file_fixture_path) && file_fixture_path
  40. fixture_set_names.map! { |f| f[fixture_path.to_s.size..-5].delete_prefix("/") }
  41. else
  42. fixture_set_names = fixture_set_names.flatten.map(&:to_s)
  43. end
  44. self.fixture_table_names |= fixture_set_names
  45. setup_fixture_accessors(fixture_set_names)
  46. end
  47. 1 def setup_fixture_accessors(fixture_set_names = nil)
  48. fixture_set_names = Array(fixture_set_names || fixture_table_names)
  49. methods = Module.new do
  50. fixture_set_names.each do |fs_name|
  51. fs_name = fs_name.to_s
  52. accessor_name = fs_name.tr("/", "_").to_sym
  53. define_method(accessor_name) do |*fixture_names|
  54. force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
  55. return_single_record = fixture_names.size == 1
  56. fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
  57. @fixture_cache[fs_name] ||= {}
  58. instances = fixture_names.map do |f_name|
  59. f_name = f_name.to_s if f_name.is_a?(Symbol)
  60. @fixture_cache[fs_name].delete(f_name) if force_reload
  61. if @loaded_fixtures[fs_name][f_name]
  62. @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
  63. else
  64. raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
  65. end
  66. end
  67. return_single_record ? instances.first : instances
  68. end
  69. private accessor_name
  70. end
  71. end
  72. include methods
  73. end
  74. 1 def uses_transaction(*methods)
  75. @uses_transaction = [] unless defined?(@uses_transaction)
  76. @uses_transaction.concat methods.map(&:to_s)
  77. end
  78. 1 def uses_transaction?(method)
  79. 32 @uses_transaction = [] unless defined?(@uses_transaction)
  80. 32 @uses_transaction.include?(method.to_s)
  81. end
  82. end
  83. 1 def run_in_transaction?
  84. 32 use_transactional_tests &&
  85. !self.class.uses_transaction?(name)
  86. end
  87. 1 def setup_fixtures(config = ActiveRecord::Base)
  88. 16 if pre_loaded_fixtures && !use_transactional_tests
  89. raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
  90. end
  91. 16 @fixture_cache = {}
  92. 16 @fixture_connections = []
  93. 16 @@already_loaded_fixtures ||= {}
  94. 16 @connection_subscriber = nil
  95. # Load fixtures once and begin transaction.
  96. 16 if run_in_transaction?
  97. 16 if @@already_loaded_fixtures[self.class]
  98. 6 @loaded_fixtures = @@already_loaded_fixtures[self.class]
  99. else
  100. 10 @loaded_fixtures = load_fixtures(config)
  101. 10 @@already_loaded_fixtures[self.class] = @loaded_fixtures
  102. end
  103. # Begin transactions for connections already established
  104. 16 @fixture_connections = enlist_fixture_connections
  105. 16 @fixture_connections.each do |connection|
  106. 16 connection.begin_transaction joinable: false, _lazy: false
  107. 16 connection.pool.lock_thread = true if lock_threads
  108. end
  109. # When connections are established in the future, begin a transaction too
  110. 16 @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
  111. spec_name = payload[:spec_name] if payload.key?(:spec_name)
  112. shard = payload[:shard] if payload.key?(:shard)
  113. setup_shared_connection_pool
  114. if spec_name
  115. begin
  116. connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard: shard)
  117. rescue ConnectionNotEstablished
  118. connection = nil
  119. end
  120. if connection && !@fixture_connections.include?(connection)
  121. connection.begin_transaction joinable: false, _lazy: false
  122. connection.pool.lock_thread = true if lock_threads
  123. @fixture_connections << connection
  124. end
  125. end
  126. end
  127. # Load fixtures for every test.
  128. else
  129. ActiveRecord::FixtureSet.reset_cache
  130. @@already_loaded_fixtures[self.class] = nil
  131. @loaded_fixtures = load_fixtures(config)
  132. end
  133. # Instantiate fixtures for every test if requested.
  134. 16 instantiate_fixtures if use_instantiated_fixtures
  135. end
  136. 1 def teardown_fixtures
  137. # Rollback changes if a transaction is active.
  138. 16 if run_in_transaction?
  139. 16 ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
  140. 16 @fixture_connections.each do |connection|
  141. 16 connection.rollback_transaction if connection.transaction_open?
  142. 16 connection.pool.lock_thread = false
  143. end
  144. 16 @fixture_connections.clear
  145. else
  146. ActiveRecord::FixtureSet.reset_cache
  147. end
  148. 16 ActiveRecord::Base.clear_active_connections!
  149. end
  150. 1 def enlist_fixture_connections
  151. 16 setup_shared_connection_pool
  152. 16 ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
  153. end
  154. 1 private
  155. # Shares the writing connection pool with connections on
  156. # other handlers.
  157. #
  158. # In an application with a primary and replica the test fixtures
  159. # need to share a connection pool so that the reading connection
  160. # can see data in the open transaction on the writing connection.
  161. 1 def setup_shared_connection_pool
  162. 16 if ActiveRecord::Base.legacy_connection_handling
  163. writing_handler = ActiveRecord::Base.connection_handlers[ActiveRecord::Base.writing_role]
  164. ActiveRecord::Base.connection_handlers.values.each do |handler|
  165. if handler != writing_handler
  166. handler.connection_pool_names.each do |name|
  167. writing_pool_manager = writing_handler.send(:owner_to_pool_manager)[name]
  168. return unless writing_pool_manager
  169. pool_manager = handler.send(:owner_to_pool_manager)[name]
  170. pool_manager.shard_names.each do |shard_name|
  171. writing_pool_config = writing_pool_manager.get_pool_config(nil, shard_name)
  172. pool_manager.set_pool_config(nil, shard_name, writing_pool_config)
  173. end
  174. end
  175. end
  176. end
  177. else
  178. 16 handler = ActiveRecord::Base.connection_handler
  179. 16 handler.connection_pool_names.each do |name|
  180. 16 pool_manager = handler.send(:owner_to_pool_manager)[name]
  181. 16 pool_manager.shard_names.each do |shard_name|
  182. 16 writing_pool_config = pool_manager.get_pool_config(ActiveRecord::Base.writing_role, shard_name)
  183. 16 pool_manager.role_names.each do |role|
  184. 16 next unless pool_manager.get_pool_config(role, shard_name)
  185. 16 pool_manager.set_pool_config(role, shard_name, writing_pool_config)
  186. end
  187. end
  188. end
  189. end
  190. end
  191. 1 def load_fixtures(config)
  192. 10 ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config).index_by(&:name)
  193. end
  194. 1 def instantiate_fixtures
  195. if pre_loaded_fixtures
  196. raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
  197. ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
  198. else
  199. raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
  200. @loaded_fixtures.each_value do |fixture_set|
  201. ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
  202. end
  203. end
  204. end
  205. 1 def load_instances?
  206. use_instantiated_fixtures != :no_instances
  207. end
  208. end
  209. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/configurable.rb

96.88% lines covered

32 relevant lines. 31 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/concern"
  3. 1 require "active_support/ordered_options"
  4. 1 module ActiveSupport
  5. # Configurable provides a <tt>config</tt> method to store and retrieve
  6. # configuration options as an <tt>OrderedOptions</tt>.
  7. 1 module Configurable
  8. 1 extend ActiveSupport::Concern
  9. 1 class Configuration < ActiveSupport::InheritableOptions
  10. 1 def compile_methods!
  11. 3 self.class.compile_methods!(keys)
  12. end
  13. # Compiles reader methods so we don't have to go through method_missing.
  14. 1 def self.compile_methods!(keys)
  15. 32 keys.reject { |m| method_defined?(m) }.each do |key|
  16. 18 class_eval <<-RUBY, __FILE__, __LINE__ + 1
  17. def #{key}; _get(#{key.inspect}); end
  18. RUBY
  19. end
  20. end
  21. end
  22. 1 module ClassMethods
  23. 1 def config
  24. 69 @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
  25. 6 superclass.config.inheritable_copy
  26. else
  27. # create a new "anonymous" class that will host the compiled reader methods
  28. 1 Class.new(Configuration).new
  29. end
  30. end
  31. 1 def configure
  32. yield config
  33. end
  34. # Allows you to add shortcut so that you don't have to refer to attribute
  35. # through config. Also look at the example for config to contrast.
  36. #
  37. # Defines both class and instance config accessors.
  38. #
  39. # class User
  40. # include ActiveSupport::Configurable
  41. # config_accessor :allowed_access
  42. # end
  43. #
  44. # User.allowed_access # => nil
  45. # User.allowed_access = false
  46. # User.allowed_access # => false
  47. #
  48. # user = User.new
  49. # user.allowed_access # => false
  50. # user.allowed_access = true
  51. # user.allowed_access # => true
  52. #
  53. # User.allowed_access # => false
  54. #
  55. # The attribute name must be a valid method name in Ruby.
  56. #
  57. # class User
  58. # include ActiveSupport::Configurable
  59. # config_accessor :"1_Badname"
  60. # end
  61. # # => NameError: invalid config attribute name
  62. #
  63. # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
  64. # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
  65. #
  66. # class User
  67. # include ActiveSupport::Configurable
  68. # config_accessor :allowed_access, instance_reader: false, instance_writer: false
  69. # end
  70. #
  71. # User.allowed_access = false
  72. # User.allowed_access # => false
  73. #
  74. # User.new.allowed_access = true # => NoMethodError
  75. # User.new.allowed_access # => NoMethodError
  76. #
  77. # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
  78. #
  79. # class User
  80. # include ActiveSupport::Configurable
  81. # config_accessor :allowed_access, instance_accessor: false
  82. # end
  83. #
  84. # User.allowed_access = false
  85. # User.allowed_access # => false
  86. #
  87. # User.new.allowed_access = true # => NoMethodError
  88. # User.new.allowed_access # => NoMethodError
  89. #
  90. # Also you can pass a block to set up the attribute with a default value.
  91. #
  92. # class User
  93. # include ActiveSupport::Configurable
  94. # config_accessor :hair_colors do
  95. # [:brown, :black, :blonde, :red]
  96. # end
  97. # end
  98. #
  99. # User.hair_colors # => [:brown, :black, :blonde, :red]
  100. 1 def config_accessor(*names, instance_reader: true, instance_writer: true, instance_accessor: true) # :doc:
  101. 19 names.each do |name|
  102. 29 raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name)
  103. 29 reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
  104. 29 writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
  105. 29 singleton_class.class_eval reader, __FILE__, reader_line
  106. 29 singleton_class.class_eval writer, __FILE__, writer_line
  107. 29 if instance_accessor
  108. 29 class_eval reader, __FILE__, reader_line if instance_reader
  109. 29 class_eval writer, __FILE__, writer_line if instance_writer
  110. end
  111. 29 send("#{name}=", yield) if block_given?
  112. end
  113. end
  114. 1 private :config_accessor
  115. end
  116. # Reads and writes attributes from a configuration <tt>OrderedOptions</tt>.
  117. #
  118. # require "active_support/configurable"
  119. #
  120. # class User
  121. # include ActiveSupport::Configurable
  122. # end
  123. #
  124. # user = User.new
  125. #
  126. # user.config.allowed_access = true
  127. # user.config.level = 1
  128. #
  129. # user.config.allowed_access # => true
  130. # user.config.level # => 1
  131. 1 def config
  132. 2 @_config ||= self.class.config.inheritable_copy
  133. end
  134. end
  135. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/current_attributes.rb

50.0% lines covered

58 relevant lines. 29 lines covered and 29 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/callbacks"
  3. 1 require "active_support/core_ext/enumerable"
  4. 1 module ActiveSupport
  5. # Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
  6. # before and after each request. This allows you to keep all the per-request attributes easily
  7. # available to the whole system.
  8. #
  9. # The following full app-like example demonstrates how to use a Current class to
  10. # facilitate easy access to the global, per-request attributes without passing them deeply
  11. # around everywhere:
  12. #
  13. # # app/models/current.rb
  14. # class Current < ActiveSupport::CurrentAttributes
  15. # attribute :account, :user
  16. # attribute :request_id, :user_agent, :ip_address
  17. #
  18. # resets { Time.zone = nil }
  19. #
  20. # def user=(user)
  21. # super
  22. # self.account = user.account
  23. # Time.zone = user.time_zone
  24. # end
  25. # end
  26. #
  27. # # app/controllers/concerns/authentication.rb
  28. # module Authentication
  29. # extend ActiveSupport::Concern
  30. #
  31. # included do
  32. # before_action :authenticate
  33. # end
  34. #
  35. # private
  36. # def authenticate
  37. # if authenticated_user = User.find_by(id: cookies.encrypted[:user_id])
  38. # Current.user = authenticated_user
  39. # else
  40. # redirect_to new_session_url
  41. # end
  42. # end
  43. # end
  44. #
  45. # # app/controllers/concerns/set_current_request_details.rb
  46. # module SetCurrentRequestDetails
  47. # extend ActiveSupport::Concern
  48. #
  49. # included do
  50. # before_action do
  51. # Current.request_id = request.uuid
  52. # Current.user_agent = request.user_agent
  53. # Current.ip_address = request.ip
  54. # end
  55. # end
  56. # end
  57. #
  58. # class ApplicationController < ActionController::Base
  59. # include Authentication
  60. # include SetCurrentRequestDetails
  61. # end
  62. #
  63. # class MessagesController < ApplicationController
  64. # def create
  65. # Current.account.messages.create(message_params)
  66. # end
  67. # end
  68. #
  69. # class Message < ApplicationRecord
  70. # belongs_to :creator, default: -> { Current.user }
  71. # after_create { |message| Event.create(record: message) }
  72. # end
  73. #
  74. # class Event < ApplicationRecord
  75. # before_create do
  76. # self.request_id = Current.request_id
  77. # self.user_agent = Current.user_agent
  78. # self.ip_address = Current.ip_address
  79. # end
  80. # end
  81. #
  82. # A word of caution: It's easy to overdo a global singleton like Current and tangle your model as a result.
  83. # Current should only be used for a few, top-level globals, like account, user, and request details.
  84. # The attributes stuck in Current should be used by more or less all actions on all requests. If you start
  85. # sticking controller-specific attributes in there, you're going to create a mess.
  86. 1 class CurrentAttributes
  87. 1 include ActiveSupport::Callbacks
  88. 1 define_callbacks :reset
  89. 1 class << self
  90. # Returns singleton instance for this class in this thread. If none exists, one is created.
  91. 1 def instance
  92. current_instances[current_instances_key] ||= new
  93. end
  94. # Declares one or more attributes that will be given both class and instance accessor methods.
  95. 1 def attribute(*names)
  96. generated_attribute_methods.module_eval do
  97. names.each do |name|
  98. define_method(name) do
  99. attributes[name.to_sym]
  100. end
  101. define_method("#{name}=") do |attribute|
  102. attributes[name.to_sym] = attribute
  103. end
  104. end
  105. end
  106. names.each do |name|
  107. define_singleton_method(name) do
  108. instance.public_send(name)
  109. end
  110. define_singleton_method("#{name}=") do |attribute|
  111. instance.public_send("#{name}=", attribute)
  112. end
  113. end
  114. end
  115. # Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
  116. 1 def before_reset(&block)
  117. set_callback :reset, :before, &block
  118. end
  119. # Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
  120. 1 def resets(&block)
  121. set_callback :reset, :after, &block
  122. end
  123. 1 alias_method :after_reset, :resets
  124. 1 delegate :set, :reset, to: :instance
  125. 1 def reset_all # :nodoc:
  126. 4 current_instances.each_value(&:reset)
  127. end
  128. 1 def clear_all # :nodoc:
  129. reset_all
  130. current_instances.clear
  131. end
  132. 1 private
  133. 1 def generated_attribute_methods
  134. @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
  135. end
  136. 1 def current_instances
  137. 4 Thread.current[:current_attributes_instances] ||= {}
  138. end
  139. 1 def current_instances_key
  140. @current_instances_key ||= name.to_sym
  141. end
  142. 1 def method_missing(name, *args, &block)
  143. # Caches the method definition as a singleton method of the receiver.
  144. #
  145. # By letting #delegate handle it, we avoid an enclosure that'll capture args.
  146. singleton_class.delegate name, to: :instance
  147. send(name, *args, &block)
  148. end
  149. end
  150. 1 attr_accessor :attributes
  151. 1 def initialize
  152. @attributes = {}
  153. end
  154. # Expose one or more attributes within a block. Old values are returned after the block concludes.
  155. # Example demonstrating the common use of needing to set Current attributes outside the request-cycle:
  156. #
  157. # class Chat::PublicationJob < ApplicationJob
  158. # def perform(attributes, room_number, creator)
  159. # Current.set(person: creator) do
  160. # Chat::Publisher.publish(attributes: attributes, room_number: room_number)
  161. # end
  162. # end
  163. # end
  164. 1 def set(set_attributes)
  165. old_attributes = compute_attributes(set_attributes.keys)
  166. assign_attributes(set_attributes)
  167. yield
  168. ensure
  169. assign_attributes(old_attributes)
  170. end
  171. # Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
  172. 1 def reset
  173. run_callbacks :reset do
  174. self.attributes = {}
  175. end
  176. end
  177. 1 private
  178. 1 def assign_attributes(new_attributes)
  179. new_attributes.each { |key, value| public_send("#{key}=", value) }
  180. end
  181. 1 def compute_attributes(keys)
  182. keys.index_with { |key| public_send(key) }
  183. end
  184. end
  185. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/current_attributes/test_helper.rb

42.86% lines covered

7 relevant lines. 3 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveSupport::CurrentAttributes::TestHelper # :nodoc:
  3. 1 def before_setup
  4. ActiveSupport::CurrentAttributes.reset_all
  5. super
  6. end
  7. 1 def after_teardown
  8. super
  9. ActiveSupport::CurrentAttributes.reset_all
  10. end
  11. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/test_case.rb

73.77% lines covered

61 relevant lines. 45 lines covered and 16 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 gem "minitest" # make sure we get the gem, not stdlib
  3. 1 require "minitest"
  4. 1 require "active_support/testing/tagged_logging"
  5. 1 require "active_support/testing/setup_and_teardown"
  6. 1 require "active_support/testing/assertions"
  7. 1 require "active_support/testing/deprecation"
  8. 1 require "active_support/testing/declarative"
  9. 1 require "active_support/testing/isolation"
  10. 1 require "active_support/testing/constant_lookup"
  11. 1 require "active_support/testing/time_helpers"
  12. 1 require "active_support/testing/file_fixtures"
  13. 1 require "active_support/testing/parallelization"
  14. 1 require "concurrent/utility/processor_counter"
  15. 1 module ActiveSupport
  16. 1 class TestCase < ::Minitest::Test
  17. 1 Assertion = Minitest::Assertion
  18. 1 class << self
  19. # Sets the order in which test cases are run.
  20. #
  21. # ActiveSupport::TestCase.test_order = :random # => :random
  22. #
  23. # Valid values are:
  24. # * +:random+ (to run tests in random order)
  25. # * +:parallel+ (to run tests in parallel)
  26. # * +:sorted+ (to run tests alphabetically by method name)
  27. # * +:alpha+ (equivalent to +:sorted+)
  28. 1 def test_order=(new_order)
  29. ActiveSupport.test_order = new_order
  30. end
  31. # Returns the order in which test cases are run.
  32. #
  33. # ActiveSupport::TestCase.test_order # => :random
  34. #
  35. # Possible values are +:random+, +:parallel+, +:alpha+, +:sorted+.
  36. # Defaults to +:random+.
  37. 1 def test_order
  38. ActiveSupport.test_order ||= :random
  39. end
  40. # Parallelizes the test suite.
  41. #
  42. # Takes a +workers+ argument that controls how many times the process
  43. # is forked. For each process a new database will be created suffixed
  44. # with the worker number.
  45. #
  46. # test-database-0
  47. # test-database-1
  48. #
  49. # If <tt>ENV["PARALLEL_WORKERS"]</tt> is set the workers argument will be ignored
  50. # and the environment variable will be used instead. This is useful for CI
  51. # environments, or other environments where you may need more workers than
  52. # you do for local testing.
  53. #
  54. # If the number of workers is set to +1+ or fewer, the tests will not be
  55. # parallelized.
  56. #
  57. # If +workers+ is set to +:number_of_processors+, the number of workers will be
  58. # set to the actual core count on the machine you are on.
  59. #
  60. # The default parallelization method is to fork processes. If you'd like to
  61. # use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+
  62. # method. Note the threaded parallelization does not create multiple
  63. # database and will not work with system tests at this time.
  64. #
  65. # parallelize(workers: :number_of_processors, with: :threads)
  66. #
  67. # The threaded parallelization uses minitest's parallel executor directly.
  68. # The processes parallelization uses a Ruby DRb server.
  69. 1 def parallelize(workers: :number_of_processors, with: :processes)
  70. workers = Concurrent.physical_processor_count if workers == :number_of_processors
  71. workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
  72. return if workers <= 1
  73. executor = case with
  74. when :processes
  75. Testing::Parallelization.new(workers)
  76. when :threads
  77. Minitest::Parallel::Executor.new(workers)
  78. else
  79. raise ArgumentError, "#{with} is not a supported parallelization executor."
  80. end
  81. self.lock_threads = false if defined?(self.lock_threads) && with == :threads
  82. Minitest.parallel_executor = executor
  83. parallelize_me!
  84. end
  85. # Set up hook for parallel testing. This can be used if you have multiple
  86. # databases or any behavior that needs to be run after the process is forked
  87. # but before the tests run.
  88. #
  89. # Note: this feature is not available with the threaded parallelization.
  90. #
  91. # In your +test_helper.rb+ add the following:
  92. #
  93. # class ActiveSupport::TestCase
  94. # parallelize_setup do
  95. # # create databases
  96. # end
  97. # end
  98. 1 def parallelize_setup(&block)
  99. ActiveSupport::Testing::Parallelization.after_fork_hook do |worker|
  100. yield worker
  101. end
  102. end
  103. # Clean up hook for parallel testing. This can be used to drop databases
  104. # if your app uses multiple write/read databases or other clean up before
  105. # the tests finish. This runs before the forked process is closed.
  106. #
  107. # Note: this feature is not available with the threaded parallelization.
  108. #
  109. # In your +test_helper.rb+ add the following:
  110. #
  111. # class ActiveSupport::TestCase
  112. # parallelize_teardown do
  113. # # drop databases
  114. # end
  115. # end
  116. 1 def parallelize_teardown(&block)
  117. ActiveSupport::Testing::Parallelization.run_cleanup_hook do |worker|
  118. yield worker
  119. end
  120. end
  121. end
  122. 1 alias_method :method_name, :name
  123. 1 include ActiveSupport::Testing::TaggedLogging
  124. 1 prepend ActiveSupport::Testing::SetupAndTeardown
  125. 1 include ActiveSupport::Testing::Assertions
  126. 1 include ActiveSupport::Testing::Deprecation
  127. 1 include ActiveSupport::Testing::TimeHelpers
  128. 1 include ActiveSupport::Testing::FileFixtures
  129. 1 extend ActiveSupport::Testing::Declarative
  130. # test/unit backwards compatibility methods
  131. 1 alias :assert_raise :assert_raises
  132. 1 alias :assert_not_empty :refute_empty
  133. 1 alias :assert_not_equal :refute_equal
  134. 1 alias :assert_not_in_delta :refute_in_delta
  135. 1 alias :assert_not_in_epsilon :refute_in_epsilon
  136. 1 alias :assert_not_includes :refute_includes
  137. 1 alias :assert_not_instance_of :refute_instance_of
  138. 1 alias :assert_not_kind_of :refute_kind_of
  139. 1 alias :assert_no_match :refute_match
  140. 1 alias :assert_not_nil :refute_nil
  141. 1 alias :assert_not_operator :refute_operator
  142. 1 alias :assert_not_predicate :refute_predicate
  143. 1 alias :assert_not_respond_to :refute_respond_to
  144. 1 alias :assert_not_same :refute_same
  145. 1 ActiveSupport.run_load_hooks(:active_support_test_case, self)
  146. end
  147. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/assertions.rb

18.97% lines covered

58 relevant lines. 11 lines covered and 47 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/enumerable"
  3. 1 module ActiveSupport
  4. 1 module Testing
  5. 1 module Assertions
  6. 1 UNTRACKED = Object.new # :nodoc:
  7. # Asserts that an expression is not truthy. Passes if <tt>object</tt> is
  8. # +nil+ or +false+. "Truthy" means "considered true in a conditional"
  9. # like <tt>if foo</tt>.
  10. #
  11. # assert_not nil # => true
  12. # assert_not false # => true
  13. # assert_not 'foo' # => Expected "foo" to be nil or false
  14. #
  15. # An error message can be specified.
  16. #
  17. # assert_not foo, 'foo should be false'
  18. 1 def assert_not(object, message = nil)
  19. message ||= "Expected #{mu_pp(object)} to be nil or false"
  20. assert !object, message
  21. end
  22. # Assertion that the block should not raise an exception.
  23. #
  24. # Passes if evaluated code in the yielded block raises no exception.
  25. #
  26. # assert_nothing_raised do
  27. # perform_service(param: 'no_exception')
  28. # end
  29. 1 def assert_nothing_raised
  30. yield
  31. rescue => error
  32. raise Minitest::UnexpectedError.new(error)
  33. end
  34. # Test numeric difference between the return value of an expression as a
  35. # result of what is evaluated in the yielded block.
  36. #
  37. # assert_difference 'Article.count' do
  38. # post :create, params: { article: {...} }
  39. # end
  40. #
  41. # An arbitrary expression is passed in and evaluated.
  42. #
  43. # assert_difference 'Article.last.comments(:reload).size' do
  44. # post :create, params: { comment: {...} }
  45. # end
  46. #
  47. # An arbitrary positive or negative difference can be specified.
  48. # The default is <tt>1</tt>.
  49. #
  50. # assert_difference 'Article.count', -1 do
  51. # post :delete, params: { id: ... }
  52. # end
  53. #
  54. # An array of expressions can also be passed in and evaluated.
  55. #
  56. # assert_difference [ 'Article.count', 'Post.count' ], 2 do
  57. # post :create, params: { article: {...} }
  58. # end
  59. #
  60. # A hash of expressions/numeric differences can also be passed in and evaluated.
  61. #
  62. # assert_difference ->{ Article.count } => 1, ->{ Notification.count } => 2 do
  63. # post :create, params: { article: {...} }
  64. # end
  65. #
  66. # A lambda or a list of lambdas can be passed in and evaluated:
  67. #
  68. # assert_difference ->{ Article.count }, 2 do
  69. # post :create, params: { article: {...} }
  70. # end
  71. #
  72. # assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
  73. # post :create, params: { article: {...} }
  74. # end
  75. #
  76. # An error message can be specified.
  77. #
  78. # assert_difference 'Article.count', -1, 'An Article should be destroyed' do
  79. # post :delete, params: { id: ... }
  80. # end
  81. 1 def assert_difference(expression, *args, &block)
  82. expressions =
  83. if expression.is_a?(Hash)
  84. message = args[0]
  85. expression
  86. else
  87. difference = args[0] || 1
  88. message = args[1]
  89. Array(expression).index_with(difference)
  90. end
  91. exps = expressions.keys.map { |e|
  92. e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
  93. }
  94. before = exps.map(&:call)
  95. retval = assert_nothing_raised(&block)
  96. expressions.zip(exps, before) do |(code, diff), exp, before_value|
  97. error = "#{code.inspect} didn't change by #{diff}"
  98. error = "#{message}.\n#{error}" if message
  99. assert_equal(before_value + diff, exp.call, error)
  100. end
  101. retval
  102. end
  103. # Assertion that the numeric result of evaluating an expression is not
  104. # changed before and after invoking the passed in block.
  105. #
  106. # assert_no_difference 'Article.count' do
  107. # post :create, params: { article: invalid_attributes }
  108. # end
  109. #
  110. # A lambda can be passed in and evaluated.
  111. #
  112. # assert_no_difference -> { Article.count } do
  113. # post :create, params: { article: invalid_attributes }
  114. # end
  115. #
  116. # An error message can be specified.
  117. #
  118. # assert_no_difference 'Article.count', 'An Article should not be created' do
  119. # post :create, params: { article: invalid_attributes }
  120. # end
  121. #
  122. # An array of expressions can also be passed in and evaluated.
  123. #
  124. # assert_no_difference [ 'Article.count', -> { Post.count } ] do
  125. # post :create, params: { article: invalid_attributes }
  126. # end
  127. 1 def assert_no_difference(expression, message = nil, &block)
  128. assert_difference expression, 0, message, &block
  129. end
  130. # Assertion that the result of evaluating an expression is changed before
  131. # and after invoking the passed in block.
  132. #
  133. # assert_changes 'Status.all_good?' do
  134. # post :create, params: { status: { ok: false } }
  135. # end
  136. #
  137. # You can pass the block as a string to be evaluated in the context of
  138. # the block. A lambda can be passed for the block as well.
  139. #
  140. # assert_changes -> { Status.all_good? } do
  141. # post :create, params: { status: { ok: false } }
  142. # end
  143. #
  144. # The assertion is useful to test side effects. The passed block can be
  145. # anything that can be converted to string with #to_s.
  146. #
  147. # assert_changes :@object do
  148. # @object = 42
  149. # end
  150. #
  151. # The keyword arguments :from and :to can be given to specify the
  152. # expected initial value and the expected value after the block was
  153. # executed.
  154. #
  155. # assert_changes :@object, from: nil, to: :foo do
  156. # @object = :foo
  157. # end
  158. #
  159. # An error message can be specified.
  160. #
  161. # assert_changes -> { Status.all_good? }, 'Expected the status to be bad' do
  162. # post :create, params: { status: { incident: true } }
  163. # end
  164. 1 def assert_changes(expression, message = nil, from: UNTRACKED, to: UNTRACKED, &block)
  165. exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
  166. before = exp.call
  167. retval = assert_nothing_raised(&block)
  168. unless from == UNTRACKED
  169. error = "Expected change from #{from.inspect}"
  170. error = "#{message}.\n#{error}" if message
  171. assert from === before, error
  172. end
  173. after = exp.call
  174. error = "#{expression.inspect} didn't change"
  175. error = "#{error}. It was already #{to}" if before == to
  176. error = "#{message}.\n#{error}" if message
  177. assert_not_equal before, after, error
  178. unless to == UNTRACKED
  179. error = "Expected change to #{to}\n"
  180. error = "#{message}.\n#{error}" if message
  181. assert to === after, error
  182. end
  183. retval
  184. end
  185. # Assertion that the result of evaluating an expression is not changed before
  186. # and after invoking the passed in block.
  187. #
  188. # assert_no_changes 'Status.all_good?' do
  189. # post :create, params: { status: { ok: true } }
  190. # end
  191. #
  192. # An error message can be specified.
  193. #
  194. # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
  195. # post :create, params: { status: { ok: false } }
  196. # end
  197. 1 def assert_no_changes(expression, message = nil, &block)
  198. exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
  199. before = exp.call
  200. retval = assert_nothing_raised(&block)
  201. after = exp.call
  202. error = "#{expression.inspect} changed"
  203. error = "#{message}.\n#{error}" if message
  204. if before.nil?
  205. assert_nil after, error
  206. else
  207. assert_equal before, after, error
  208. end
  209. retval
  210. end
  211. end
  212. end
  213. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/constant_lookup.rb

57.14% lines covered

14 relevant lines. 8 lines covered and 6 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/concern"
  3. 1 require "active_support/inflector"
  4. 1 module ActiveSupport
  5. 1 module Testing
  6. # Resolves a constant from a minitest spec name.
  7. #
  8. # Given the following spec-style test:
  9. #
  10. # describe WidgetsController, :index do
  11. # describe "authenticated user" do
  12. # describe "returns widgets" do
  13. # it "has a controller that exists" do
  14. # assert_kind_of WidgetsController, @controller
  15. # end
  16. # end
  17. # end
  18. # end
  19. #
  20. # The test will have the following name:
  21. #
  22. # "WidgetsController::index::authenticated user::returns widgets"
  23. #
  24. # The constant WidgetsController can be resolved from the name.
  25. # The following code will resolve the constant:
  26. #
  27. # controller = determine_constant_from_test_name(name) do |constant|
  28. # Class === constant && constant < ::ActionController::Metal
  29. # end
  30. 1 module ConstantLookup
  31. 1 extend ::ActiveSupport::Concern
  32. 1 module ClassMethods # :nodoc:
  33. 1 def determine_constant_from_test_name(test_name)
  34. names = test_name.split "::"
  35. while names.size > 0 do
  36. names.last.sub!(/Test$/, "")
  37. begin
  38. constant = names.join("::").safe_constantize
  39. break(constant) if yield(constant)
  40. ensure
  41. names.pop
  42. end
  43. end
  44. end
  45. end
  46. end
  47. end
  48. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/declarative.rb

41.67% lines covered

12 relevant lines. 5 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveSupport
  3. 1 module Testing
  4. 1 module Declarative
  5. 1 unless defined?(Spec)
  6. # Helper to define a test method using a String. Under the hood, it replaces
  7. # spaces with underscores and defines the test method.
  8. #
  9. # test "verify something" do
  10. # ...
  11. # end
  12. 1 def test(name, &block)
  13. test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
  14. defined = method_defined? test_name
  15. raise "#{test_name} is already defined in #{self}" if defined
  16. if block_given?
  17. define_method(test_name, &block)
  18. else
  19. define_method(test_name) do
  20. flunk "No implementation provided for #{name}"
  21. end
  22. end
  23. end
  24. end
  25. end
  26. end
  27. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/deprecation.rb

29.17% lines covered

24 relevant lines. 7 lines covered and 17 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/deprecation"
  3. 1 module ActiveSupport
  4. 1 module Testing
  5. 1 module Deprecation #:nodoc:
  6. 1 def assert_deprecated(match = nil, deprecator = nil, &block)
  7. result, warnings = collect_deprecations(deprecator, &block)
  8. assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
  9. if match
  10. match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
  11. assert warnings.any? { |w| match.match?(w) }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
  12. end
  13. result
  14. end
  15. 1 def assert_not_deprecated(deprecator = nil, &block)
  16. result, deprecations = collect_deprecations(deprecator, &block)
  17. assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
  18. result
  19. end
  20. 1 def collect_deprecations(deprecator = nil)
  21. deprecator ||= ActiveSupport::Deprecation
  22. old_behavior = deprecator.behavior
  23. deprecations = []
  24. deprecator.behavior = Proc.new do |message, callstack|
  25. deprecations << message
  26. end
  27. result = yield
  28. [result, deprecations]
  29. ensure
  30. deprecator.behavior = old_behavior
  31. end
  32. end
  33. end
  34. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/file_fixtures.rb

61.54% lines covered

13 relevant lines. 8 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/concern"
  3. 1 module ActiveSupport
  4. 1 module Testing
  5. # Adds simple access to sample files called file fixtures.
  6. # File fixtures are normal files stored in
  7. # <tt>ActiveSupport::TestCase.file_fixture_path</tt>.
  8. #
  9. # File fixtures are represented as +Pathname+ objects.
  10. # This makes it easy to extract specific information:
  11. #
  12. # file_fixture("example.txt").read # get the file's content
  13. # file_fixture("example.mp3").size # get the file size
  14. 1 module FileFixtures
  15. 1 extend ActiveSupport::Concern
  16. 1 included do
  17. 14 class_attribute :file_fixture_path, instance_writer: false
  18. end
  19. # Returns a +Pathname+ to the fixture file named +fixture_name+.
  20. #
  21. # Raises +ArgumentError+ if +fixture_name+ can't be found.
  22. 1 def file_fixture(fixture_name)
  23. path = Pathname.new(File.join(file_fixture_path, fixture_name))
  24. if path.exist?
  25. path
  26. else
  27. msg = "the directory '%s' does not contain a file named '%s'"
  28. raise ArgumentError, msg % [file_fixture_path, fixture_name]
  29. end
  30. end
  31. end
  32. end
  33. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/isolation.rb

24.14% lines covered

58 relevant lines. 14 lines covered and 44 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveSupport
  3. 1 module Testing
  4. 1 module Isolation
  5. 1 require "thread"
  6. 1 def self.included(klass) #:nodoc:
  7. klass.class_eval do
  8. parallelize_me!
  9. end
  10. end
  11. 1 def self.forking_env?
  12. 1 !ENV["NO_FORK"] && Process.respond_to?(:fork)
  13. end
  14. 1 def run
  15. serialized = run_in_isolation do
  16. super
  17. end
  18. Marshal.load(serialized)
  19. end
  20. 1 module Forking
  21. 1 def run_in_isolation(&blk)
  22. read, write = IO.pipe
  23. read.binmode
  24. write.binmode
  25. pid = fork do
  26. read.close
  27. yield
  28. begin
  29. if error?
  30. failures.map! { |e|
  31. begin
  32. Marshal.dump e
  33. e
  34. rescue TypeError
  35. ex = Exception.new e.message
  36. ex.set_backtrace e.backtrace
  37. Minitest::UnexpectedError.new ex
  38. end
  39. }
  40. end
  41. test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
  42. result = Marshal.dump(test_result)
  43. end
  44. write.puts [result].pack("m")
  45. exit!
  46. end
  47. write.close
  48. result = read.read
  49. Process.wait2(pid)
  50. result.unpack1("m")
  51. end
  52. end
  53. 1 module Subprocess
  54. 1 ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
  55. # Crazy H4X to get this working in windows / jruby with
  56. # no forking.
  57. 1 def run_in_isolation(&blk)
  58. require "tempfile"
  59. if ENV["ISOLATION_TEST"]
  60. yield
  61. test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
  62. File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
  63. file.puts [Marshal.dump(test_result)].pack("m")
  64. end
  65. exit!
  66. else
  67. Tempfile.open("isolation") do |tmpfile|
  68. env = {
  69. "ISOLATION_TEST" => self.class.name,
  70. "ISOLATION_OUTPUT" => tmpfile.path
  71. }
  72. test_opts = "-n#{self.class.name}##{name}"
  73. load_path_args = []
  74. $-I.each do |p|
  75. load_path_args << "-I"
  76. load_path_args << File.expand_path(p)
  77. end
  78. child = IO.popen([env, Gem.ruby, *load_path_args, $0, *ORIG_ARGV, test_opts])
  79. begin
  80. Process.wait(child.pid)
  81. rescue Errno::ECHILD # The child process may exit before we wait
  82. nil
  83. end
  84. return tmpfile.read.unpack1("m")
  85. end
  86. end
  87. end
  88. end
  89. 1 include forking_env? ? Forking : Subprocess
  90. end
  91. end
  92. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization.rb

62.07% lines covered

29 relevant lines. 18 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "drb"
  3. 1 require "drb/unix" unless Gem.win_platform?
  4. 1 require "active_support/core_ext/module/attribute_accessors"
  5. 1 require "active_support/testing/parallelization/server"
  6. 1 require "active_support/testing/parallelization/worker"
  7. 1 module ActiveSupport
  8. 1 module Testing
  9. 1 class Parallelization # :nodoc:
  10. 1 @@after_fork_hooks = []
  11. 1 def self.after_fork_hook(&blk)
  12. @@after_fork_hooks << blk
  13. end
  14. 1 cattr_reader :after_fork_hooks
  15. 1 @@run_cleanup_hooks = []
  16. 1 def self.run_cleanup_hook(&blk)
  17. @@run_cleanup_hooks << blk
  18. end
  19. 1 cattr_reader :run_cleanup_hooks
  20. 1 def initialize(worker_count)
  21. @worker_count = worker_count
  22. @queue_server = Server.new
  23. @worker_pool = []
  24. @url = DRb.start_service("drbunix:", @queue_server).uri
  25. end
  26. 1 def start
  27. @worker_pool = @worker_count.times.map do |worker|
  28. Worker.new(worker, @url).start
  29. end
  30. end
  31. 1 def <<(work)
  32. @queue_server << work
  33. end
  34. 1 def shutdown
  35. @queue_server.shutdown
  36. @worker_pool.each { |pid| Process.waitpid pid }
  37. end
  38. end
  39. end
  40. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization/server.rb

35.71% lines covered

42 relevant lines. 15 lines covered and 27 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "drb"
  3. 1 require "drb/unix" unless Gem.win_platform?
  4. 1 module ActiveSupport
  5. 1 module Testing
  6. 1 class Parallelization # :nodoc:
  7. 1 class Server
  8. 1 include DRb::DRbUndumped
  9. 1 def initialize
  10. @queue = Queue.new
  11. @active_workers = Concurrent::Map.new
  12. @in_flight = Concurrent::Map.new
  13. end
  14. 1 def record(reporter, result)
  15. raise DRb::DRbConnError if result.is_a?(DRb::DRbUnknown)
  16. @in_flight.delete([result.klass, result.name])
  17. reporter.synchronize do
  18. reporter.record(result)
  19. end
  20. end
  21. 1 def <<(o)
  22. o[2] = DRbObject.new(o[2]) if o
  23. @queue << o
  24. end
  25. 1 def pop
  26. if test = @queue.pop
  27. @in_flight[[test[0].to_s, test[1]]] = test
  28. test
  29. end
  30. end
  31. 1 def start_worker(worker_id)
  32. @active_workers[worker_id] = true
  33. end
  34. 1 def stop_worker(worker_id)
  35. @active_workers.delete(worker_id)
  36. end
  37. 1 def active_workers?
  38. @active_workers.size > 0
  39. end
  40. 1 def shutdown
  41. # Wait for initial queue to drain
  42. while @queue.length != 0
  43. sleep 0.1
  44. end
  45. @queue.close
  46. # Wait until all workers have finished
  47. while active_workers?
  48. sleep 0.1
  49. end
  50. @in_flight.values.each do |(klass, name, reporter)|
  51. result = Minitest::Result.from(klass.new(name))
  52. error = RuntimeError.new("result not reported")
  53. error.set_backtrace([""])
  54. result.failures << Minitest::UnexpectedError.new(error)
  55. reporter.synchronize do
  56. reporter.record(result)
  57. end
  58. end
  59. end
  60. end
  61. end
  62. end
  63. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/parallelization/worker.rb

26.92% lines covered

52 relevant lines. 14 lines covered and 38 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveSupport
  3. 1 module Testing
  4. 1 class Parallelization # :nodoc:
  5. 1 class Worker
  6. 1 def initialize(number, url)
  7. @id = SecureRandom.uuid
  8. @number = number
  9. @url = url
  10. @setup_exception = nil
  11. end
  12. 1 def start
  13. fork do
  14. set_process_title("(starting)")
  15. DRb.stop_service
  16. @queue = DRbObject.new_with_uri(@url)
  17. @queue.start_worker(@id)
  18. begin
  19. after_fork
  20. rescue => @setup_exception; end
  21. work_from_queue
  22. ensure
  23. set_process_title("(stopping)")
  24. run_cleanup
  25. @queue.stop_worker(@id)
  26. end
  27. end
  28. 1 def work_from_queue
  29. while job = @queue.pop
  30. perform_job(job)
  31. end
  32. end
  33. 1 def perform_job(job)
  34. klass = job[0]
  35. method = job[1]
  36. reporter = job[2]
  37. set_process_title("#{klass}##{method}")
  38. result = klass.with_info_handler reporter do
  39. Minitest.run_one_method(klass, method)
  40. end
  41. safe_record(reporter, result)
  42. end
  43. 1 def safe_record(reporter, result)
  44. add_setup_exception(result) if @setup_exception
  45. begin
  46. @queue.record(reporter, result)
  47. rescue DRb::DRbConnError
  48. result.failures.map! do |failure|
  49. if failure.respond_to?(:error)
  50. # minitest >5.14.0
  51. error = DRb::DRbRemoteError.new(failure.error)
  52. else
  53. error = DRb::DRbRemoteError.new(failure.exception)
  54. end
  55. Minitest::UnexpectedError.new(error)
  56. end
  57. @queue.record(reporter, result)
  58. end
  59. set_process_title("(idle)")
  60. end
  61. 1 def after_fork
  62. Parallelization.after_fork_hooks.each do |cb|
  63. cb.call(@number)
  64. end
  65. end
  66. 1 def run_cleanup
  67. Parallelization.run_cleanup_hooks.each do |cb|
  68. cb.call(@number)
  69. end
  70. end
  71. 1 private
  72. 1 def add_setup_exception(result)
  73. result.failures.prepend Minitest::UnexpectedError.new(@setup_exception)
  74. end
  75. 1 def set_process_title(status)
  76. Process.setproctitle("Rails test worker #{@number} - #{status}")
  77. end
  78. end
  79. end
  80. end
  81. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/setup_and_teardown.rb

75.0% lines covered

20 relevant lines. 15 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/callbacks"
  3. 1 module ActiveSupport
  4. 1 module Testing
  5. # Adds support for +setup+ and +teardown+ callbacks.
  6. # These callbacks serve as a replacement to overwriting the
  7. # <tt>#setup</tt> and <tt>#teardown</tt> methods of your TestCase.
  8. #
  9. # class ExampleTest < ActiveSupport::TestCase
  10. # setup do
  11. # # ...
  12. # end
  13. #
  14. # teardown do
  15. # # ...
  16. # end
  17. # end
  18. 1 module SetupAndTeardown
  19. 1 def self.prepended(klass)
  20. 1 klass.include ActiveSupport::Callbacks
  21. 1 klass.define_callbacks :setup, :teardown
  22. 1 klass.extend ClassMethods
  23. end
  24. 1 module ClassMethods
  25. # Add a callback, which runs before <tt>TestCase#setup</tt>.
  26. 1 def setup(*args, &block)
  27. 5 set_callback(:setup, :before, *args, &block)
  28. end
  29. # Add a callback, which runs after <tt>TestCase#teardown</tt>.
  30. 1 def teardown(*args, &block)
  31. 2 set_callback(:teardown, :after, *args, &block)
  32. end
  33. end
  34. 1 def before_setup # :nodoc:
  35. super
  36. run_callbacks :setup
  37. end
  38. 1 def after_teardown # :nodoc:
  39. begin
  40. run_callbacks :teardown
  41. rescue => e
  42. self.failures << Minitest::UnexpectedError.new(e)
  43. end
  44. super
  45. end
  46. end
  47. end
  48. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/tagged_logging.rb

46.67% lines covered

15 relevant lines. 7 lines covered and 8 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module ActiveSupport
  3. 1 module Testing
  4. # Logs a "PostsControllerTest: test name" heading before each test to
  5. # make test.log easier to search and follow along with.
  6. 1 module TaggedLogging #:nodoc:
  7. 1 attr_writer :tagged_logger
  8. 1 def before_setup
  9. if tagged_logger && tagged_logger.info?
  10. heading = "#{self.class}: #{name}"
  11. divider = "-" * heading.size
  12. tagged_logger.info divider
  13. tagged_logger.info heading
  14. tagged_logger.info divider
  15. end
  16. super
  17. end
  18. 1 private
  19. 1 def tagged_logger
  20. @tagged_logger ||= (defined?(Rails.logger) && Rails.logger)
  21. end
  22. end
  23. end
  24. end

vender/bundle/ruby/2.6.0/gems/activesupport-6.1.3/lib/active_support/testing/time_helpers.rb

37.7% lines covered

61 relevant lines. 23 lines covered and 38 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/module/redefine_method"
  3. 1 require "active_support/core_ext/time/calculations"
  4. 1 require "concurrent/map"
  5. 1 module ActiveSupport
  6. 1 module Testing
  7. # Manages stubs for TimeHelpers
  8. 1 class SimpleStubs # :nodoc:
  9. 1 Stub = Struct.new(:object, :method_name, :original_method)
  10. 1 def initialize
  11. @stubs = Concurrent::Map.new { |h, k| h[k] = {} }
  12. end
  13. # Stubs object.method_name with the given block
  14. # If the method is already stubbed, remove that stub
  15. # so that removing this stub will restore the original implementation.
  16. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  17. # target = Time.zone.local(2004, 11, 24, 1, 4, 44)
  18. # simple_stubs.stub_object(Time, :now) { at(target.to_i) }
  19. # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
  20. 1 def stub_object(object, method_name, &block)
  21. if stub = stubbing(object, method_name)
  22. unstub_object(stub)
  23. end
  24. new_name = "__simple_stub__#{method_name}"
  25. @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
  26. object.singleton_class.alias_method new_name, method_name
  27. object.define_singleton_method(method_name, &block)
  28. end
  29. # Remove all object-method stubs held by this instance
  30. 1 def unstub_all!
  31. @stubs.each_value do |object_stubs|
  32. object_stubs.each_value do |stub|
  33. unstub_object(stub)
  34. end
  35. end
  36. @stubs.clear
  37. end
  38. # Returns the Stub for object#method_name
  39. # (nil if it is not stubbed)
  40. 1 def stubbing(object, method_name)
  41. @stubs[object.object_id][method_name]
  42. end
  43. # Returns true if any stubs are set, false if there are none
  44. 1 def stubbed?
  45. !@stubs.empty?
  46. end
  47. 1 private
  48. # Restores the original object.method described by the Stub
  49. 1 def unstub_object(stub)
  50. singleton_class = stub.object.singleton_class
  51. singleton_class.silence_redefinition_of_method stub.method_name
  52. singleton_class.alias_method stub.method_name, stub.original_method
  53. singleton_class.undef_method stub.original_method
  54. end
  55. end
  56. # Contains helpers that help you test passage of time.
  57. 1 module TimeHelpers
  58. 1 def after_teardown
  59. travel_back
  60. super
  61. end
  62. # Changes current time to the time in the future or in the past by a given time difference by
  63. # stubbing +Time.now+, +Date.today+, and +DateTime.now+. The stubs are automatically removed
  64. # at the end of the test.
  65. #
  66. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  67. # travel 1.day
  68. # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
  69. # Date.current # => Sun, 10 Nov 2013
  70. # DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
  71. #
  72. # This method also accepts a block, which will return the current time back to its original
  73. # state at the end of the block:
  74. #
  75. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  76. # travel 1.day do
  77. # User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
  78. # end
  79. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  80. 1 def travel(duration, &block)
  81. travel_to Time.now + duration, &block
  82. end
  83. # Changes current time to the given time by stubbing +Time.now+,
  84. # +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
  85. # The stubs are automatically removed at the end of the test.
  86. #
  87. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  88. # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
  89. # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
  90. # Date.current # => Wed, 24 Nov 2004
  91. # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
  92. #
  93. # Dates are taken as their timestamp at the beginning of the day in the
  94. # application time zone. <tt>Time.current</tt> returns said timestamp,
  95. # and <tt>Time.now</tt> its equivalent in the system time zone. Similarly,
  96. # <tt>Date.current</tt> returns a date equal to the argument, and
  97. # <tt>Date.today</tt> the date according to <tt>Time.now</tt>, which may
  98. # be different. (Note that you rarely want to deal with <tt>Time.now</tt>,
  99. # or <tt>Date.today</tt>, in order to honor the application time zone
  100. # please always use <tt>Time.current</tt> and <tt>Date.current</tt>.)
  101. #
  102. # Note that the usec for the time passed will be set to 0 to prevent rounding
  103. # errors with external services, like MySQL (which will round instead of floor,
  104. # leading to off-by-one-second errors).
  105. #
  106. # This method also accepts a block, which will return the current time back to its original
  107. # state at the end of the block:
  108. #
  109. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  110. # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) do
  111. # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
  112. # end
  113. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  114. 1 def travel_to(date_or_time)
  115. if block_given? && simple_stubs.stubbing(Time, :now)
  116. travel_to_nested_block_call = <<~MSG
  117. Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
  118. Instead of:
  119. travel_to 2.days.from_now do
  120. # 2 days from today
  121. travel_to 3.days.from_now do
  122. # 5 days from today
  123. end
  124. end
  125. preferred way to achieve above is:
  126. travel 2.days do
  127. # 2 days from today
  128. end
  129. travel 5.days do
  130. # 5 days from today
  131. end
  132. MSG
  133. raise travel_to_nested_block_call
  134. end
  135. if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
  136. now = date_or_time.midnight.to_time
  137. else
  138. now = date_or_time.to_time.change(usec: 0)
  139. end
  140. simple_stubs.stub_object(Time, :now) { at(now.to_i) }
  141. simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
  142. simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
  143. if block_given?
  144. begin
  145. yield
  146. ensure
  147. travel_back
  148. end
  149. end
  150. end
  151. # Returns the current time back to its original state, by removing the stubs added by
  152. # +travel+, +travel_to+, and +freeze_time+.
  153. #
  154. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  155. #
  156. # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
  157. # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
  158. #
  159. # travel_back
  160. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  161. #
  162. # This method also accepts a block, which brings the stubs back at the end of the block:
  163. #
  164. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  165. #
  166. # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
  167. # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
  168. #
  169. # travel_back do
  170. # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
  171. # end
  172. #
  173. # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
  174. 1 def travel_back
  175. stubbed_time = Time.current if block_given? && simple_stubs.stubbed?
  176. simple_stubs.unstub_all!
  177. yield if block_given?
  178. ensure
  179. travel_to stubbed_time if stubbed_time
  180. end
  181. 1 alias_method :unfreeze_time, :travel_back
  182. # Calls +travel_to+ with +Time.now+.
  183. #
  184. # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
  185. # freeze_time
  186. # sleep(1)
  187. # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
  188. #
  189. # This method also accepts a block, which will return the current time back to its original
  190. # state at the end of the block:
  191. #
  192. # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
  193. # freeze_time do
  194. # sleep(1)
  195. # User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00
  196. # end
  197. # Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00
  198. 1 def freeze_time(&block)
  199. travel_to Time.now, &block
  200. end
  201. 1 private
  202. 1 def simple_stubs
  203. @simple_stubs ||= SimpleStubs.new
  204. end
  205. end
  206. end
  207. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay.rb

75.0% lines covered

40 relevant lines. 30 lines covered and 10 lines missed.
    
  1. # encoding: utf-8
  2. # Encoding.default_internal = 'UTF-8'
  3. # = CodeRay Library
  4. #
  5. # CodeRay is a Ruby library for syntax highlighting.
  6. #
  7. # I try to make CodeRay easy to use and intuitive, but at the same time fully
  8. # featured, complete, fast and efficient.
  9. #
  10. # See README.
  11. #
  12. # It consists mainly of
  13. # * the main engine: CodeRay (Scanners::Scanner, Tokens, Encoders::Encoder)
  14. # * the plugin system: PluginHost, Plugin
  15. # * the scanners in CodeRay::Scanners
  16. # * the encoders in CodeRay::Encoders
  17. # * the styles in CodeRay::Styles
  18. #
  19. # Here's a fancy graphic to light up this gray docu:
  20. #
  21. # http://cycnus.de/raindark/coderay/scheme.png
  22. #
  23. # == Documentation
  24. #
  25. # See CodeRay, Encoders, Scanners, Tokens.
  26. #
  27. # == Usage
  28. #
  29. # Remember you need RubyGems to use CodeRay, unless you have it in your load
  30. # path. Run Ruby with -rubygems option if required.
  31. #
  32. # === Highlight Ruby code in a string as html
  33. #
  34. # require 'coderay'
  35. # print CodeRay.scan('puts "Hello, world!"', :ruby).html
  36. #
  37. # # prints something like this:
  38. # puts <span class="s">&quot;Hello, world!&quot;</span>
  39. #
  40. #
  41. # === Highlight C code from a file in a html div
  42. #
  43. # require 'coderay'
  44. # print CodeRay.scan(File.read('ruby.h'), :c).div
  45. # print CodeRay.scan_file('ruby.h').html.div
  46. #
  47. # You can include this div in your page. The used CSS styles can be printed with
  48. #
  49. # % coderay_stylesheet
  50. #
  51. # === Highlight without typing too much
  52. #
  53. # If you are one of the hasty (or lazy, or extremely curious) people, just run this file:
  54. #
  55. # % ruby -rubygems /path/to/coderay/coderay.rb > example.html
  56. #
  57. # and look at the file it created in your browser.
  58. #
  59. # = CodeRay Module
  60. #
  61. # The CodeRay module provides convenience methods for the engine.
  62. #
  63. # * The +lang+ and +format+ arguments select Scanner and Encoder to use. These are
  64. # simply lower-case symbols, like <tt>:python</tt> or <tt>:html</tt>.
  65. # * All methods take an optional hash as last parameter, +options+, that is send to
  66. # the Encoder / Scanner.
  67. # * Input and language are always sorted in this order: +code+, +lang+.
  68. # (This is in alphabetical order, if you need a mnemonic ;)
  69. #
  70. # You should be able to highlight everything you want just using these methods;
  71. # so there is no need to dive into CodeRay's deep class hierarchy.
  72. #
  73. # The examples in the demo directory demonstrate common cases using this interface.
  74. #
  75. # = Basic Access Ways
  76. #
  77. # Read this to get a general view what CodeRay provides.
  78. #
  79. # == Scanning
  80. #
  81. # Scanning means analysing an input string, splitting it up into Tokens.
  82. # Each Token knows about what type it is: string, comment, class name, etc.
  83. #
  84. # Each +lang+ (language) has its own Scanner; for example, <tt>:ruby</tt> code is
  85. # handled by CodeRay::Scanners::Ruby.
  86. #
  87. # CodeRay.scan:: Scan a string in a given language into Tokens.
  88. # This is the most common method to use.
  89. # CodeRay.scan_file:: Scan a file and guess the language using FileType.
  90. #
  91. # The Tokens object you get from these methods can encode itself; see Tokens.
  92. #
  93. # == Encoding
  94. #
  95. # Encoding means compiling Tokens into an output. This can be colored HTML or
  96. # LaTeX, a textual statistic or just the number of non-whitespace tokens.
  97. #
  98. # Each Encoder provides output in a specific +format+, so you select Encoders via
  99. # formats like <tt>:html</tt> or <tt>:statistic</tt>.
  100. #
  101. # CodeRay.encode:: Scan and encode a string in a given language.
  102. # CodeRay.encode_tokens:: Encode the given tokens.
  103. # CodeRay.encode_file:: Scan a file, guess the language using FileType and encode it.
  104. #
  105. # == All-in-One Encoding
  106. #
  107. # CodeRay.encode:: Highlight a string with a given input and output format.
  108. #
  109. # == Instanciating
  110. #
  111. # You can use an Encoder instance to highlight multiple inputs. This way, the setup
  112. # for this Encoder must only be done once.
  113. #
  114. # CodeRay.encoder:: Create an Encoder instance with format and options.
  115. # CodeRay.scanner:: Create an Scanner instance for lang, with '' as default code.
  116. #
  117. # To make use of CodeRay.scanner, use CodeRay::Scanner::code=.
  118. #
  119. # The scanning methods provide more flexibility; we recommend to use these.
  120. #
  121. # == Reusing Scanners and Encoders
  122. #
  123. # If you want to re-use scanners and encoders (because that is faster), see
  124. # CodeRay::Duo for the most convenient (and recommended) interface.
  125. 1 module CodeRay
  126. 1 $CODERAY_DEBUG ||= false
  127. 1 CODERAY_PATH = File.expand_path('../coderay', __FILE__)
  128. # Assuming the path is a subpath of lib/coderay/
  129. 1 def self.coderay_path *path
  130. 15 File.join CODERAY_PATH, *path
  131. end
  132. 1 autoload :VERSION, 'coderay/version'
  133. # helpers
  134. 1 autoload :FileType, coderay_path('helpers', 'file_type')
  135. # Tokens
  136. 1 autoload :Tokens, coderay_path('tokens')
  137. 1 autoload :TokensProxy, coderay_path('tokens_proxy')
  138. 1 autoload :TokenKinds, coderay_path('token_kinds')
  139. # Plugin system
  140. 1 autoload :PluginHost, coderay_path('helpers', 'plugin_host')
  141. 1 autoload :Plugin, coderay_path('helpers', 'plugin')
  142. # Plugins
  143. 1 autoload :Scanners, coderay_path('scanners')
  144. 1 autoload :Encoders, coderay_path('encoders')
  145. 1 autoload :Styles, coderay_path('styles')
  146. # convenience access and reusable Encoder/Scanner pair
  147. 1 autoload :Duo, coderay_path('duo')
  148. 1 class << self
  149. # Scans the given +code+ (a String) with the Scanner for +lang+.
  150. #
  151. # This is a simple way to use CodeRay. Example:
  152. # require 'coderay'
  153. # page = CodeRay.scan("puts 'Hello, world!'", :ruby).html
  154. #
  155. # See also demo/demo_simple.
  156. 1 def scan code, lang, options = {}, &block
  157. CodeRay::TokensProxy.new code, lang, options, block
  158. end
  159. # Scans +filename+ (a path to a code file) with the Scanner for +lang+.
  160. #
  161. # If +lang+ is :auto or omitted, the CodeRay::FileType module is used to
  162. # determine it. If it cannot find out what type it is, it uses
  163. # CodeRay::Scanners::Text.
  164. #
  165. # Calls CodeRay.scan.
  166. #
  167. # Example:
  168. # require 'coderay'
  169. # page = CodeRay.scan_file('some_c_code.c').html
  170. 1 def scan_file filename, lang = :auto, options = {}, &block
  171. lang = CodeRay::FileType.fetch filename, :text, true if lang == :auto
  172. code = File.read filename
  173. scan code, lang, options, &block
  174. end
  175. # Encode a string.
  176. #
  177. # This scans +code+ with the the Scanner for +lang+ and then
  178. # encodes it with the Encoder for +format+.
  179. # +options+ will be passed to the Encoder.
  180. #
  181. # See CodeRay::Encoder.encode.
  182. 1 def encode code, lang, format, options = {}
  183. 6 encoder(format, options).encode code, lang, options
  184. end
  185. # Encode pre-scanned Tokens.
  186. # Use this together with CodeRay.scan:
  187. #
  188. # require 'coderay'
  189. #
  190. # # Highlight a short Ruby code example in a HTML span
  191. # tokens = CodeRay.scan '1 + 2', :ruby
  192. # puts CodeRay.encode_tokens(tokens, :span)
  193. #
  194. 1 def encode_tokens tokens, format, options = {}
  195. encoder(format, options).encode_tokens tokens, options
  196. end
  197. # Encodes +filename+ (a path to a code file) with the Scanner for +lang+.
  198. #
  199. # See CodeRay.scan_file.
  200. # Notice that the second argument is the output +format+, not the input language.
  201. #
  202. # Example:
  203. # require 'coderay'
  204. # page = CodeRay.encode_file 'some_c_code.c', :html
  205. 1 def encode_file filename, format, options = {}
  206. tokens = scan_file filename, :auto, get_scanner_options(options)
  207. encode_tokens tokens, format, options
  208. end
  209. # Highlight a string into a HTML <div>.
  210. #
  211. # CSS styles use classes, so you have to include a stylesheet
  212. # in your output.
  213. #
  214. # See encode.
  215. 1 def highlight code, lang, options = { :css => :class }, format = :div
  216. encode code, lang, format, options
  217. end
  218. # Highlight a file into a HTML <div>.
  219. #
  220. # CSS styles use classes, so you have to include a stylesheet
  221. # in your output.
  222. #
  223. # See encode.
  224. 1 def highlight_file filename, options = { :css => :class }, format = :div
  225. encode_file filename, format, options
  226. end
  227. # Finds the Encoder class for +format+ and creates an instance, passing
  228. # +options+ to it.
  229. #
  230. # Example:
  231. # require 'coderay'
  232. #
  233. # stats = CodeRay.encoder(:statistic)
  234. # stats.encode("puts 17 + 4\n", :ruby)
  235. #
  236. # puts '%d out of %d tokens have the kind :integer.' % [
  237. # stats.type_stats[:integer].count,
  238. # stats.real_token_count
  239. # ]
  240. # #-> 2 out of 4 tokens have the kind :integer.
  241. 1 def encoder format, options = {}
  242. 6 CodeRay::Encoders[format].new options
  243. end
  244. # Finds the Scanner class for +lang+ and creates an instance, passing
  245. # +options+ to it.
  246. #
  247. # See Scanner.new.
  248. 1 def scanner lang, options = {}, &block
  249. CodeRay::Scanners[lang].new '', options, &block
  250. end
  251. # Extract the options for the scanner from the +options+ hash.
  252. #
  253. # Returns an empty Hash if <tt>:scanner_options</tt> is not set.
  254. #
  255. # This is used if a method like CodeRay.encode has to provide options
  256. # for Encoder _and_ scanner.
  257. 1 def get_scanner_options options
  258. 6 options.fetch :scanner_options, {}
  259. end
  260. end
  261. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. 1 module CodeRay
  2. # This module holds the Encoder class and its subclasses.
  3. # For example, the HTML encoder is named CodeRay::Encoders::HTML
  4. # can be found in coderay/encoders/html.
  5. #
  6. # Encoders also provides methods and constants for the register
  7. # mechanism and the [] method that returns the Encoder class
  8. # belonging to the given format.
  9. 1 module Encoders
  10. 1 extend PluginHost
  11. 1 plugin_path File.dirname(__FILE__), 'encoders'
  12. 1 autoload :Encoder, CodeRay.coderay_path('encoders', 'encoder')
  13. end
  14. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/_map.rb

100.0% lines covered

3 relevant lines. 3 lines covered and 0 lines missed.
    
  1. 1 module CodeRay
  2. 1 module Encoders
  3. 1 map \
  4. :loc => :lines_of_code,
  5. :plain => :text,
  6. :plaintext => :text,
  7. :remove_comments => :comment_filter,
  8. :stats => :statistic,
  9. :term => :terminal,
  10. :tty => :terminal,
  11. :yml => :yaml
  12. # No default because Tokens#nonsense should raise NoMethodError.
  13. end
  14. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/encoder.rb

54.79% lines covered

73 relevant lines. 40 lines covered and 33 lines missed.
    
  1. 1 module CodeRay
  2. 1 module Encoders
  3. # = Encoder
  4. #
  5. # The Encoder base class. Together with Scanner and
  6. # Tokens, it forms the highlighting triad.
  7. #
  8. # Encoder instances take a Tokens object and do something with it.
  9. #
  10. # The most common Encoder is surely the HTML encoder
  11. # (CodeRay::Encoders::HTML). It highlights the code in a colorful
  12. # html page.
  13. # If you want the highlighted code in a div or a span instead,
  14. # use its subclasses Div and Span.
  15. 1 class Encoder
  16. 1 extend Plugin
  17. 1 plugin_host Encoders
  18. 1 class << self
  19. # If FILE_EXTENSION isn't defined, this method returns the
  20. # downcase class name instead.
  21. 1 def const_missing sym
  22. if sym == :FILE_EXTENSION
  23. (defined?(@plugin_id) && @plugin_id || name[/\w+$/].downcase).to_s
  24. else
  25. super
  26. end
  27. end
  28. # The default file extension for output file of this encoder class.
  29. 1 def file_extension
  30. self::FILE_EXTENSION
  31. end
  32. end
  33. # Subclasses are to store their default options in this constant.
  34. 1 DEFAULT_OPTIONS = { }
  35. # The options you gave the Encoder at creating.
  36. 1 attr_accessor :options, :scanner
  37. # Creates a new Encoder.
  38. # +options+ is saved and used for all encode operations, as long
  39. # as you don't overwrite it there by passing additional options.
  40. #
  41. # Encoder objects provide three encode methods:
  42. # - encode simply takes a +code+ string and a +lang+
  43. # - encode_tokens expects a +tokens+ object instead
  44. #
  45. # Each method has an optional +options+ parameter. These are
  46. # added to the options you passed at creation.
  47. 1 def initialize options = {}
  48. 6 @options = self.class::DEFAULT_OPTIONS.merge options
  49. 6 @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = false
  50. end
  51. # Encode a Tokens object.
  52. 1 def encode_tokens tokens, options = {}
  53. options = @options.merge options
  54. @scanner = tokens.scanner if tokens.respond_to? :scanner
  55. setup options
  56. compile tokens, options
  57. finish options
  58. end
  59. # Encode the given +code+ using the Scanner for +lang+.
  60. 1 def encode code, lang, options = {}
  61. 6 options = @options.merge options
  62. 6 @scanner = Scanners[lang].new code, CodeRay.get_scanner_options(options).update(:tokens => self)
  63. 6 setup options
  64. 6 @scanner.tokenize
  65. 6 finish options
  66. end
  67. # You can use highlight instead of encode, if that seems
  68. # more clear to you.
  69. 1 alias highlight encode
  70. # The default file extension for this encoder.
  71. 1 def file_extension
  72. self.class.file_extension
  73. end
  74. 1 def << token
  75. unless @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN
  76. warn 'Using old Tokens#<< interface.'
  77. @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = true
  78. end
  79. self.token(*token)
  80. end
  81. # Called with +content+ and +kind+ of the currently scanned token.
  82. # For simple scanners, it's enougth to implement this method.
  83. #
  84. # By default, it calls text_token, begin_group, end_group, begin_line,
  85. # or end_line, depending on the +content+.
  86. 1 def token content, kind
  87. case content
  88. when String
  89. text_token content, kind
  90. when :begin_group
  91. begin_group kind
  92. when :end_group
  93. end_group kind
  94. when :begin_line
  95. begin_line kind
  96. when :end_line
  97. end_line kind
  98. else
  99. raise ArgumentError, 'Unknown token content type: %p, kind = %p' % [content, kind]
  100. end
  101. end
  102. # Called for each text token ([text, kind]), where text is a String.
  103. 1 def text_token text, kind
  104. @out << text
  105. end
  106. # Starts a token group with the given +kind+.
  107. 1 def begin_group kind
  108. end
  109. # Ends a token group with the given +kind+.
  110. 1 def end_group kind
  111. end
  112. # Starts a new line token group with the given +kind+.
  113. 1 def begin_line kind
  114. end
  115. # Ends a new line token group with the given +kind+.
  116. 1 def end_line kind
  117. end
  118. 1 protected
  119. # Called with merged options before encoding starts.
  120. # Sets @out to an empty string.
  121. #
  122. # See the HTML Encoder for an example of option caching.
  123. 1 def setup options
  124. 6 @out = get_output(options)
  125. end
  126. 1 def get_output options
  127. 6 options[:out] || ''.dup
  128. end
  129. # Append data.to_s to the output. Returns the argument.
  130. 1 def output data
  131. @out << data.to_s
  132. data
  133. end
  134. # Called with merged options after encoding starts.
  135. # The return value is the result of encoding, typically @out.
  136. 1 def finish options
  137. 6 @out
  138. end
  139. # Do the encoding.
  140. #
  141. # The already created +tokens+ object must be used; it must be a
  142. # Tokens object.
  143. 1 def compile tokens, options = {}
  144. content = nil
  145. for item in tokens
  146. if item.is_a? Array
  147. raise ArgumentError, 'Two-element array tokens are no longer supported.'
  148. end
  149. if content
  150. token content, item
  151. content = nil
  152. else
  153. content = item
  154. end
  155. end
  156. raise 'odd number list for Tokens' if content
  157. end
  158. 1 alias tokens compile
  159. 1 public :tokens
  160. end
  161. end
  162. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/encoders/terminal.rb

84.78% lines covered

46 relevant lines. 39 lines covered and 7 lines missed.
    
  1. 1 module CodeRay
  2. 1 module Encoders
  3. # Outputs code highlighted for a color terminal.
  4. #
  5. # Note: This encoder is in beta. It currently doesn't use the Styles.
  6. #
  7. # Alias: +term+
  8. #
  9. # == Authors & License
  10. #
  11. # By Rob Aldred (http://robaldred.co.uk)
  12. #
  13. # Based on idea by Nathan Weizenbaum (http://nex-3.com)
  14. #
  15. # MIT License (http://www.opensource.org/licenses/mit-license.php)
  16. 1 class Terminal < Encoder
  17. 1 register_for :terminal
  18. TOKEN_COLORS = {
  19. 1 :debug => "\e[1;37;44m",
  20. :annotation => "\e[34m",
  21. :attribute_name => "\e[35m",
  22. :attribute_value => "\e[31m",
  23. :binary => {
  24. :self => "\e[31m",
  25. :char => "\e[1;31m",
  26. :delimiter => "\e[1;31m",
  27. },
  28. :char => {
  29. :self => "\e[35m",
  30. :delimiter => "\e[1;35m"
  31. },
  32. :class => "\e[1;35;4m",
  33. :class_variable => "\e[36m",
  34. :color => "\e[32m",
  35. :comment => {
  36. :self => "\e[1;30m",
  37. :char => "\e[37m",
  38. :delimiter => "\e[37m",
  39. },
  40. :constant => "\e[1;34;4m",
  41. :decorator => "\e[35m",
  42. :definition => "\e[1;33m",
  43. :directive => "\e[33m",
  44. :docstring => "\e[31m",
  45. :doctype => "\e[1;34m",
  46. :done => "\e[1;30;2m",
  47. :entity => "\e[31m",
  48. :error => "\e[1;37;41m",
  49. :exception => "\e[1;31m",
  50. :float => "\e[1;35m",
  51. :function => "\e[1;34m",
  52. :global_variable => "\e[1;32m",
  53. :hex => "\e[1;36m",
  54. :id => "\e[1;34m",
  55. :include => "\e[31m",
  56. :integer => "\e[1;34m",
  57. :imaginary => "\e[1;34m",
  58. :important => "\e[1;31m",
  59. :key => {
  60. :self => "\e[35m",
  61. :char => "\e[1;35m",
  62. :delimiter => "\e[1;35m",
  63. },
  64. :keyword => "\e[32m",
  65. :label => "\e[1;33m",
  66. :local_variable => "\e[33m",
  67. :namespace => "\e[1;35m",
  68. :octal => "\e[1;34m",
  69. :predefined => "\e[36m",
  70. :predefined_constant => "\e[1;36m",
  71. :predefined_type => "\e[1;32m",
  72. :preprocessor => "\e[1;36m",
  73. :pseudo_class => "\e[1;34m",
  74. :regexp => {
  75. :self => "\e[35m",
  76. :delimiter => "\e[1;35m",
  77. :modifier => "\e[35m",
  78. :char => "\e[1;35m",
  79. },
  80. :reserved => "\e[32m",
  81. :shell => {
  82. :self => "\e[33m",
  83. :char => "\e[1;33m",
  84. :delimiter => "\e[1;33m",
  85. :escape => "\e[1;33m",
  86. },
  87. :string => {
  88. :self => "\e[31m",
  89. :modifier => "\e[1;31m",
  90. :char => "\e[1;35m",
  91. :delimiter => "\e[1;31m",
  92. :escape => "\e[1;31m",
  93. },
  94. :symbol => {
  95. :self => "\e[33m",
  96. :delimiter => "\e[1;33m",
  97. },
  98. :tag => "\e[32m",
  99. :type => "\e[1;34m",
  100. :value => "\e[36m",
  101. :variable => "\e[34m",
  102. :insert => {
  103. :self => "\e[42m",
  104. :insert => "\e[1;32;42m",
  105. :eyecatcher => "\e[102m",
  106. },
  107. :delete => {
  108. :self => "\e[41m",
  109. :delete => "\e[1;31;41m",
  110. :eyecatcher => "\e[101m",
  111. },
  112. :change => {
  113. :self => "\e[44m",
  114. :change => "\e[37;44m",
  115. },
  116. :head => {
  117. :self => "\e[45m",
  118. :filename => "\e[37;45m"
  119. },
  120. }
  121. 1 TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved]
  122. 1 TOKEN_COLORS[:method] = TOKEN_COLORS[:function]
  123. 1 TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter]
  124. 1 protected
  125. 1 def setup(options)
  126. 6 super
  127. 6 @opened = []
  128. 6 @color_scopes = [TOKEN_COLORS]
  129. end
  130. 1 public
  131. 1 def text_token text, kind
  132. 58 if color = @color_scopes.last[kind]
  133. 10 color = color[:self] if color.is_a? Hash
  134. 10 @out << color
  135. 10 @out << (text.index("\n") ? text.gsub("\n", "\e[0m\n" + color) : text)
  136. 10 @out << "\e[0m"
  137. 10 if outer_color = @color_scopes.last[:self]
  138. 4 @out << outer_color
  139. end
  140. else
  141. 48 @out << text
  142. end
  143. end
  144. 1 def begin_group kind
  145. 2 @opened << kind
  146. 2 @out << open_token(kind)
  147. end
  148. 1 alias begin_line begin_group
  149. 1 def end_group kind
  150. 2 if @opened.pop
  151. 2 @color_scopes.pop
  152. 2 @out << "\e[0m"
  153. 2 if outer_color = @color_scopes.last[:self]
  154. @out << outer_color
  155. end
  156. end
  157. end
  158. 1 def end_line kind
  159. @out << (@line_filler ||= "\t" * 100)
  160. end_group kind
  161. end
  162. 1 private
  163. 1 def open_token kind
  164. 2 if color = @color_scopes.last[kind]
  165. 2 if color.is_a? Hash
  166. 2 @color_scopes << color
  167. 2 color[:self]
  168. else
  169. @color_scopes << @color_scopes.last
  170. color
  171. end
  172. else
  173. @color_scopes << @color_scopes.last
  174. ''
  175. end
  176. end
  177. end
  178. end
  179. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin.rb

66.67% lines covered

18 relevant lines. 12 lines covered and 6 lines missed.
    
  1. 1 module CodeRay
  2. # = Plugin
  3. #
  4. # Plugins have to include this module.
  5. #
  6. # IMPORTANT: Use extend for this module.
  7. #
  8. # See CodeRay::PluginHost for examples.
  9. 1 module Plugin
  10. 1 attr_reader :plugin_id
  11. # Register this class for the given +id+.
  12. #
  13. # Example:
  14. # class MyPlugin < PluginHost::BaseClass
  15. # register_for :my_id
  16. # ...
  17. # end
  18. #
  19. # See PluginHost.register.
  20. 1 def register_for id
  21. 2 @plugin_id = id
  22. 2 plugin_host.register self, id
  23. end
  24. # Returns the title of the plugin, or sets it to the
  25. # optional argument +title+.
  26. 1 def title title = nil
  27. if title
  28. @title = title.to_s
  29. else
  30. @title ||= name[/([^:]+)$/, 1]
  31. end
  32. end
  33. # The PluginHost for this Plugin class.
  34. 1 def plugin_host host = nil
  35. 4 if host.is_a? PluginHost
  36. 2 const_set :PLUGIN_HOST, host
  37. end
  38. 4 self::PLUGIN_HOST
  39. end
  40. 1 def aliases
  41. plugin_host.plugin_hash.inject [] do |aliases, (key, _)|
  42. aliases << key if plugin_host[key] == self
  43. aliases
  44. end
  45. end
  46. end
  47. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/plugin_host.rb

79.73% lines covered

74 relevant lines. 59 lines covered and 15 lines missed.
    
  1. 1 module CodeRay
  2. # = PluginHost
  3. #
  4. # A simple subclass/subfolder plugin system.
  5. #
  6. # Example:
  7. # class Generators
  8. # extend PluginHost
  9. # plugin_path 'app/generators'
  10. # end
  11. #
  12. # class Generator
  13. # extend Plugin
  14. # PLUGIN_HOST = Generators
  15. # end
  16. #
  17. # class FancyGenerator < Generator
  18. # register_for :fancy
  19. # end
  20. #
  21. # Generators[:fancy] #-> FancyGenerator
  22. # # or
  23. # CodeRay.require_plugin 'Generators/fancy'
  24. # # or
  25. # Generators::Fancy
  26. 1 module PluginHost
  27. # Raised if Encoders::[] fails because:
  28. # * a file could not be found
  29. # * the requested Plugin is not registered
  30. 1 PluginNotFound = Class.new LoadError
  31. 1 HostNotFound = Class.new LoadError
  32. 1 PLUGIN_HOSTS = []
  33. 1 PLUGIN_HOSTS_BY_ID = {} # dummy hash
  34. # Loads all plugins using list and load.
  35. 1 def load_all
  36. for plugin in list
  37. load plugin
  38. end
  39. end
  40. # Returns the Plugin for +id+.
  41. #
  42. # Example:
  43. # yaml_plugin = MyPluginHost[:yaml]
  44. 1 def [] id, *args, &blk
  45. 13 plugin = validate_id(id)
  46. 13 begin
  47. 13 plugin = plugin_hash.[](plugin, *args, &blk)
  48. end while plugin.is_a? String
  49. 13 plugin
  50. end
  51. 1 alias load []
  52. # Tries to +load+ the missing plugin by translating +const+ to the
  53. # underscore form (eg. LinesOfCode becomes lines_of_code).
  54. 1 def const_missing const
  55. 1 id = const.to_s.
  56. gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
  57. gsub(/([a-z\d])([A-Z])/,'\1_\2').
  58. downcase
  59. 1 load id
  60. end
  61. 1 class << self
  62. # Adds the module/class to the PLUGIN_HOSTS list.
  63. 1 def extended mod
  64. 2 PLUGIN_HOSTS << mod
  65. end
  66. end
  67. # The path where the plugins can be found.
  68. 1 def plugin_path *args
  69. 6 unless args.empty?
  70. 2 @plugin_path = File.expand_path File.join(*args)
  71. end
  72. 6 @plugin_path ||= ''
  73. end
  74. # Map a plugin_id to another.
  75. #
  76. # Usage: Put this in a file plugin_path/_map.rb.
  77. #
  78. # class MyColorHost < PluginHost
  79. # map :navy => :dark_blue,
  80. # :maroon => :brown,
  81. # :luna => :moon
  82. # end
  83. 1 def map hash
  84. 2 for from, to in hash
  85. 23 from = validate_id from
  86. 23 to = validate_id to
  87. 23 plugin_hash[from] = to unless plugin_hash.has_key? from
  88. end
  89. end
  90. # Define the default plugin to use when no plugin is found
  91. # for a given id, or return the default plugin.
  92. #
  93. # See also map.
  94. #
  95. # class MyColorHost < PluginHost
  96. # map :navy => :dark_blue
  97. # default :gray
  98. # end
  99. #
  100. # MyColorHost.default # loads and returns the Gray plugin
  101. 1 def default id = nil
  102. 1 if id
  103. 1 id = validate_id id
  104. 1 raise "The default plugin can't be named \"default\"." if id == :default
  105. 1 plugin_hash[:default] = id
  106. else
  107. load :default
  108. end
  109. end
  110. # Every plugin must register itself for +id+ by calling register_for,
  111. # which calls this method.
  112. #
  113. # See Plugin#register_for.
  114. 1 def register plugin, id
  115. 2 plugin_hash[validate_id(id)] = plugin
  116. end
  117. # A Hash of plugion_id => Plugin pairs.
  118. 1 def plugin_hash
  119. 64 @plugin_hash ||= (@plugin_hash = make_plugin_hash).tap { load_plugin_map }
  120. end
  121. # Returns an array of all .rb files in the plugin path.
  122. #
  123. # The extension .rb is not included.
  124. 1 def list
  125. Dir[path_to('*')].select do |file|
  126. File.basename(file)[/^(?!_)\w+\.rb$/]
  127. end.map do |file|
  128. File.basename(file, '.rb').to_sym
  129. end
  130. end
  131. # Returns an array of all Plugins.
  132. #
  133. # Note: This loads all plugins using load_all.
  134. 1 def all_plugins
  135. load_all
  136. plugin_hash.values.grep(Class)
  137. end
  138. # Loads the map file (see map).
  139. #
  140. # This is done automatically when plugin_path is called.
  141. 1 def load_plugin_map
  142. 2 mapfile = path_to '_map'
  143. 2 if File.exist? mapfile
  144. 2 require mapfile
  145. 2 true
  146. else
  147. false
  148. end
  149. end
  150. 1 protected
  151. # Return a plugin hash that automatically loads plugins.
  152. 1 def make_plugin_hash
  153. 2 Hash.new do |h, plugin_id|
  154. 2 id = validate_id(plugin_id)
  155. 2 path = path_to id
  156. begin
  157. 2 require path
  158. rescue LoadError => boom
  159. if h.has_key?(:default)
  160. h[:default]
  161. else
  162. raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom]
  163. end
  164. else
  165. # Plugin should have registered by now
  166. 2 if h.has_key? id
  167. 2 h[id]
  168. else
  169. raise PluginNotFound, "No #{self.name} plugin for #{id.inspect} found in #{path}."
  170. end
  171. end
  172. end
  173. end
  174. # Returns the expected path to the plugin file for the given id.
  175. 1 def path_to plugin_id
  176. 4 File.join plugin_path, "#{plugin_id}.rb"
  177. end
  178. # Converts +id+ to a valid plugin ID String, or returns +nil+.
  179. #
  180. # Raises +ArgumentError+ for all other objects, or if the
  181. # given String includes non-alphanumeric characters (\W).
  182. 1 def validate_id id
  183. 64 case id
  184. when Symbol
  185. 61 id.to_s
  186. when String
  187. 3 if id[/\w+/] == id
  188. 3 id.downcase
  189. else
  190. raise ArgumentError, "Invalid id given: #{id}"
  191. end
  192. else
  193. raise ArgumentError, "Symbol or String expected, but #{id.class} given."
  194. end
  195. end
  196. end
  197. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/helpers/word_list.rb

83.33% lines covered

12 relevant lines. 10 lines covered and 2 lines missed.
    
  1. 1 module CodeRay
  2. # = WordList
  3. #
  4. # <b>A Hash subclass designed for mapping word lists to token types.</b>
  5. #
  6. # A WordList is a Hash with some additional features.
  7. # It is intended to be used for keyword recognition.
  8. #
  9. # WordList is optimized to be used in Scanners,
  10. # typically to decide whether a given ident is a special token.
  11. #
  12. # For case insensitive words use WordList::CaseIgnoring.
  13. #
  14. # Example:
  15. #
  16. # # define word arrays
  17. # RESERVED_WORDS = %w[
  18. # asm break case continue default do else
  19. # ]
  20. #
  21. # PREDEFINED_TYPES = %w[
  22. # int long short char void
  23. # ]
  24. #
  25. # # make a WordList
  26. # IDENT_KIND = WordList.new(:ident).
  27. # add(RESERVED_WORDS, :reserved).
  28. # add(PREDEFINED_TYPES, :predefined_type)
  29. #
  30. # ...
  31. #
  32. # def scan_tokens tokens, options
  33. # ...
  34. #
  35. # elsif scan(/[A-Za-z_][A-Za-z_0-9]*/)
  36. # # use it
  37. # kind = IDENT_KIND[match]
  38. # ...
  39. 1 class WordList < Hash
  40. # Create a new WordList with +default+ as default value.
  41. 1 def initialize default = false
  42. 3 super default
  43. end
  44. # Add words to the list and associate them with +value+.
  45. #
  46. # Returns +self+, so you can concat add calls.
  47. 1 def add words, value = true
  48. 111 words.each { |word| self[word] = value }
  49. 8 self
  50. end
  51. end
  52. # A CaseIgnoring WordList is like a WordList, only that
  53. # keys are compared case-insensitively (normalizing keys using +downcase+).
  54. 1 class WordList::CaseIgnoring < WordList
  55. 1 def [] key
  56. super key.downcase
  57. end
  58. 1 def []= key, value
  59. super key.downcase, value
  60. end
  61. end
  62. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. 1 require 'strscan'
  2. 1 module CodeRay
  3. 1 autoload :WordList, coderay_path('helpers', 'word_list')
  4. # = Scanners
  5. #
  6. # This module holds the Scanner class and its subclasses.
  7. # For example, the Ruby scanner is named CodeRay::Scanners::Ruby
  8. # can be found in coderay/scanners/ruby.
  9. #
  10. # Scanner also provides methods and constants for the register
  11. # mechanism and the [] method that returns the Scanner class
  12. # belonging to the given lang.
  13. #
  14. # See PluginHost.
  15. 1 module Scanners
  16. 1 extend PluginHost
  17. 1 plugin_path File.dirname(__FILE__), 'scanners'
  18. 1 autoload :Scanner, CodeRay.coderay_path('scanners', 'scanner')
  19. end
  20. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/_map.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. 1 module CodeRay
  2. 1 module Scanners
  3. 1 map \
  4. :'c++' => :cpp,
  5. :cplusplus => :cpp,
  6. :ecmascript => :java_script,
  7. :ecma_script => :java_script,
  8. :rhtml => :erb,
  9. :eruby => :erb,
  10. :irb => :ruby,
  11. :javascript => :java_script,
  12. :js => :java_script,
  13. :pascal => :delphi,
  14. :patch => :diff,
  15. :plain => :text,
  16. :plaintext => :text,
  17. :xhtml => :html,
  18. :yml => :yaml
  19. 1 default :text
  20. end
  21. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby.rb

24.91% lines covered

269 relevant lines. 67 lines covered and 202 lines missed.
    
  1. 1 module CodeRay
  2. 1 module Scanners
  3. # This scanner is really complex, since Ruby _is_ a complex language!
  4. #
  5. # It tries to highlight 100% of all common code,
  6. # and 90% of strange codes.
  7. #
  8. # It is optimized for HTML highlighting, and is not very useful for
  9. # parsing or pretty printing.
  10. 1 class Ruby < Scanner
  11. 1 register_for :ruby
  12. 1 file_extension 'rb'
  13. 1 autoload :Patterns, CodeRay.coderay_path('scanners', 'ruby', 'patterns')
  14. 1 autoload :StringState, CodeRay.coderay_path('scanners', 'ruby', 'string_state')
  15. 1 def interpreted_string_state
  16. StringState.new :string, true, '"'
  17. end
  18. 1 protected
  19. 1 def setup
  20. 6 @state = :initial
  21. end
  22. 1 def scan_tokens encoder, options
  23. 6 state, heredocs = options[:state] || @state
  24. 6 heredocs = heredocs.dup if heredocs.is_a?(Array)
  25. 6 if state && state.instance_of?(StringState)
  26. encoder.begin_group state.type
  27. end
  28. 6 last_state = nil
  29. 6 method_call_expected = false
  30. 6 value_expected = true
  31. 6 inline_block_stack = nil
  32. 6 inline_block_curly_depth = 0
  33. 6 if heredocs
  34. state = heredocs.shift
  35. encoder.begin_group state.type
  36. heredocs = nil if heredocs.empty?
  37. end
  38. # def_object_stack = nil
  39. # def_object_paren_depth = 0
  40. 6 patterns = Patterns # avoid constant lookup
  41. 6 unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8'
  42. 6 until eos?
  43. 54 if state.instance_of? ::Symbol
  44. 54 if match = scan(/[ \t\f\v]+/)
  45. 14 encoder.text_token match, :space
  46. 40 elsif match = scan(/\n/)
  47. if heredocs
  48. unscan # heredoc scanning needs \n at start
  49. state = heredocs.shift
  50. encoder.begin_group state.type
  51. heredocs = nil if heredocs.empty?
  52. else
  53. state = :initial if state == :undef_comma_expected
  54. encoder.text_token match, :space
  55. value_expected = true
  56. end
  57. 40 elsif match = scan(bol? ? / \#(!)?.* | #{patterns::RUBYDOC_OR_DATA} /ox : /\#.*/)
  58. encoder.text_token match, self[1] ? :doctype : :comment
  59. 40 elsif match = scan(/\\\n/)
  60. if heredocs
  61. unscan # heredoc scanning needs \n at start
  62. encoder.text_token scan(/\\/), :space
  63. state = heredocs.shift
  64. encoder.begin_group state.type
  65. heredocs = nil if heredocs.empty?
  66. else
  67. encoder.text_token match, :space
  68. end
  69. 40 elsif state == :initial
  70. # IDENTS #
  71. 40 if !method_call_expected &&
  72. 30 match = scan(unicode ? /#{patterns::METHOD_NAME}/uo :
  73. /#{patterns::METHOD_NAME}/o)
  74. 14 kind = patterns::IDENT_KIND[match]
  75. 14 if value_expected != :colon_expected && scan(/:(?!:)/)
  76. value_expected = true
  77. encoder.text_token match, :key
  78. encoder.text_token ':', :operator
  79. else
  80. 14 value_expected = false
  81. 14 if kind == :ident
  82. 8 if match[/\A[A-Z]/] && !(match[/[!?]$/] || match?(/\(/))
  83. kind = :constant
  84. end
  85. 6 elsif kind == :keyword
  86. 4 state = patterns::KEYWORD_NEW_STATE[match]
  87. 4 if patterns::KEYWORDS_EXPECTING_VALUE[match]
  88. 2 value_expected = match == 'when' ? :colon_expected : true
  89. end
  90. end
  91. 14 value_expected = true if !value_expected && check(/#{patterns::VALUE_FOLLOWS}/o)
  92. 14 encoder.text_token match, kind
  93. end
  94. 26 elsif method_call_expected &&
  95. 10 match = scan(unicode ? /#{patterns::METHOD_AFTER_DOT}/uo :
  96. /#{patterns::METHOD_AFTER_DOT}/o)
  97. 10 if method_call_expected == '::' && match[/\A[A-Z]/] && !match?(/\(/)
  98. encoder.text_token match, :constant
  99. else
  100. 10 encoder.text_token match, :ident
  101. end
  102. 10 method_call_expected = false
  103. 10 value_expected = check(/#{patterns::VALUE_FOLLOWS}/o)
  104. # OPERATORS #
  105. 16 elsif !method_call_expected && match = scan(/ (\.(?!\.)|::) | ( \.\.\.? | ==?=? | [,\(\[\{] ) | [\)\]\}] /x)
  106. 14 method_call_expected = self[1]
  107. 14 value_expected = !method_call_expected && !!self[2]
  108. 14 if inline_block_stack
  109. case match
  110. when '{'
  111. inline_block_curly_depth += 1
  112. when '}'
  113. inline_block_curly_depth -= 1
  114. if inline_block_curly_depth == 0 # closing brace of inline block reached
  115. state, inline_block_curly_depth, heredocs = inline_block_stack.pop
  116. inline_block_stack = nil if inline_block_stack.empty?
  117. heredocs = nil if heredocs && heredocs.empty?
  118. encoder.text_token match, :inline_delimiter
  119. encoder.end_group :inline
  120. next
  121. end
  122. end
  123. end
  124. 14 encoder.text_token match, :operator
  125. 2 elsif match = scan(unicode ? /#{patterns::SYMBOL}/uo :
  126. /#{patterns::SYMBOL}/o)
  127. case delim = match[1]
  128. when ?', ?"
  129. encoder.begin_group :symbol
  130. encoder.text_token ':', :symbol
  131. match = delim.chr
  132. encoder.text_token match, :delimiter
  133. state = self.class::StringState.new :symbol, delim == ?", match
  134. else
  135. encoder.text_token match, :symbol
  136. value_expected = false
  137. end
  138. 2 elsif match = scan(/ ' (?:(?>[^'\\]*) ')? | " (?:(?>[^"\\\#]*) ")? /mx)
  139. 2 if match.size == 1
  140. kind = check(self.class::StringState.simple_key_pattern(match)) ? :key : :string
  141. encoder.begin_group kind
  142. encoder.text_token match, :delimiter
  143. state = self.class::StringState.new kind, match == '"', match # important for streaming
  144. else
  145. 2 kind = value_expected == true && scan(/:/) ? :key : :string
  146. 2 encoder.begin_group kind
  147. 2 encoder.text_token match[0,1], :delimiter
  148. 2 encoder.text_token match[1..-2], :content if match.size > 2
  149. 2 encoder.text_token match[-1,1], :delimiter
  150. 2 encoder.end_group kind
  151. 2 encoder.text_token ':', :operator if kind == :key
  152. 2 value_expected = false
  153. end
  154. elsif match = scan(unicode ? /#{patterns::INSTANCE_VARIABLE}/uo :
  155. /#{patterns::INSTANCE_VARIABLE}/o)
  156. value_expected = false
  157. encoder.text_token match, :instance_variable
  158. elsif value_expected && match = scan(/\//)
  159. encoder.begin_group :regexp
  160. encoder.text_token match, :delimiter
  161. state = self.class::StringState.new :regexp, true, '/'
  162. elsif match = scan(value_expected ? /[-+]?#{patterns::NUMERIC}/o : /#{patterns::NUMERIC}/o)
  163. if method_call_expected
  164. encoder.text_token match, :error
  165. method_call_expected = false
  166. else
  167. kind = self[1] ? :float : :integer # TODO: send :hex/:octal/:binary
  168. match << 'r' if match !~ /e/i && scan(/r/)
  169. match << 'i' if scan(/i/)
  170. encoder.text_token match, kind
  171. end
  172. value_expected = false
  173. elsif match = scan(/ [-+!~^\/]=? | [:;] | &\. | [*|&]{1,2}=? | >>? /x)
  174. value_expected = true
  175. encoder.text_token match, :operator
  176. elsif value_expected && match = scan(/#{patterns::HEREDOC_OPEN}/o)
  177. quote = self[3]
  178. delim = self[quote ? 4 : 2]
  179. kind = patterns::QUOTE_TO_TYPE[quote]
  180. encoder.begin_group kind
  181. encoder.text_token match, :delimiter
  182. encoder.end_group kind
  183. heredocs ||= [] # create heredocs if empty
  184. heredocs << self.class::StringState.new(kind, quote != "'", delim,
  185. self[1] ? :indented : :linestart)
  186. value_expected = false
  187. elsif value_expected && match = scan(/#{patterns::FANCY_STRING_START}/o)
  188. kind = patterns::FANCY_STRING_KIND[self[1]]
  189. encoder.begin_group kind
  190. state = self.class::StringState.new kind, patterns::FANCY_STRING_INTERPRETED[self[1]], self[2]
  191. encoder.text_token match, :delimiter
  192. elsif value_expected && match = scan(/#{patterns::CHARACTER}/o)
  193. value_expected = false
  194. encoder.text_token match, :integer
  195. elsif match = scan(/ %=? | <(?:<|=>?)? | \? /x)
  196. value_expected = match == '?' ? :colon_expected : true
  197. encoder.text_token match, :operator
  198. elsif match = scan(/`/)
  199. encoder.begin_group :shell
  200. encoder.text_token match, :delimiter
  201. state = self.class::StringState.new :shell, true, match
  202. elsif match = scan(unicode ? /#{patterns::GLOBAL_VARIABLE}/uo :
  203. /#{patterns::GLOBAL_VARIABLE}/o)
  204. encoder.text_token match, :global_variable
  205. value_expected = false
  206. elsif match = scan(unicode ? /#{patterns::CLASS_VARIABLE}/uo :
  207. /#{patterns::CLASS_VARIABLE}/o)
  208. encoder.text_token match, :class_variable
  209. value_expected = false
  210. elsif match = scan(/\\\z/)
  211. encoder.text_token match, :space
  212. else
  213. if method_call_expected
  214. method_call_expected = false
  215. next
  216. end
  217. unless unicode
  218. # check for unicode
  219. $DEBUG_BEFORE, $DEBUG = $DEBUG, false
  220. begin
  221. if check(/./mu).size > 1
  222. # seems like we should try again with unicode
  223. unicode = true
  224. end
  225. rescue
  226. # bad unicode char; use getch
  227. ensure
  228. $DEBUG = $DEBUG_BEFORE
  229. end
  230. next if unicode
  231. end
  232. encoder.text_token getch, :error
  233. end
  234. 40 if last_state
  235. state = last_state unless state.is_a?(StringState) # otherwise, a simple 'def"' results in unclosed tokens
  236. last_state = nil
  237. end
  238. elsif state == :def_expected
  239. if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo :
  240. /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o)
  241. encoder.text_token match, :method
  242. state = :initial
  243. else
  244. last_state = :dot_expected
  245. state = :initial
  246. end
  247. elsif state == :dot_expected
  248. if match = scan(/\.|::/)
  249. # invalid definition
  250. state = :def_expected
  251. encoder.text_token match, :operator
  252. else
  253. state = :initial
  254. end
  255. elsif state == :module_expected
  256. if match = scan(/<</)
  257. encoder.text_token match, :operator
  258. else
  259. state = :initial
  260. if match = scan(unicode ? / (?:#{patterns::IDENT}::)* #{patterns::IDENT} /oux :
  261. / (?:#{patterns::IDENT}::)* #{patterns::IDENT} /ox)
  262. encoder.text_token match, :class
  263. end
  264. end
  265. elsif state == :undef_expected
  266. state = :undef_comma_expected
  267. if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo :
  268. /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o)
  269. encoder.text_token match, :method
  270. elsif match = scan(/#{patterns::SYMBOL}/o)
  271. case delim = match[1]
  272. when ?', ?"
  273. encoder.begin_group :symbol
  274. encoder.text_token ':', :symbol
  275. match = delim.chr
  276. encoder.text_token match, :delimiter
  277. state = self.class::StringState.new :symbol, delim == ?", match
  278. state.next_state = :undef_comma_expected
  279. else
  280. encoder.text_token match, :symbol
  281. end
  282. else
  283. state = :initial
  284. end
  285. elsif state == :undef_comma_expected
  286. if match = scan(/,/)
  287. encoder.text_token match, :operator
  288. state = :undef_expected
  289. else
  290. state = :initial
  291. end
  292. elsif state == :alias_expected
  293. match = scan(unicode ? /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/uo :
  294. /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/o)
  295. if match
  296. encoder.text_token self[1], (self[1][0] == ?: ? :symbol : :method)
  297. encoder.text_token self[2], :space
  298. encoder.text_token self[3], (self[3][0] == ?: ? :symbol : :method)
  299. end
  300. state = :initial
  301. else
  302. skipped #:nocov:
  303. skipped raise_inspect 'Unknown state: %p' % [state], encoder
  304. skipped #:nocov:
  305. end
  306. else # StringState
  307. match = scan_until(state.pattern) || scan_rest
  308. unless match.empty?
  309. encoder.text_token match, :content
  310. break if eos?
  311. end
  312. if state.heredoc && self[1] # end of heredoc
  313. match = getch
  314. match << scan_until(/$/) unless eos?
  315. encoder.text_token match, :delimiter unless match.empty?
  316. encoder.end_group state.type
  317. state = state.next_state
  318. next
  319. end
  320. case match = getch
  321. when state.delim
  322. if state.paren_depth
  323. state.paren_depth -= 1
  324. if state.paren_depth > 0
  325. encoder.text_token match, :content
  326. next
  327. end
  328. end
  329. encoder.text_token match, :delimiter
  330. if state.type == :regexp && !eos?
  331. match = scan(/#{patterns::REGEXP_MODIFIERS}/o)
  332. encoder.text_token match, :modifier unless match.empty?
  333. end
  334. encoder.end_group state.type
  335. value_expected = false
  336. state = state.next_state
  337. when '\\'
  338. if state.interpreted
  339. if esc = scan(/#{patterns::ESCAPE}/o)
  340. encoder.text_token match + esc, :char
  341. else
  342. encoder.text_token match, :error
  343. end
  344. else
  345. case esc = getch
  346. when nil
  347. encoder.text_token match, :content
  348. when state.delim, '\\'
  349. encoder.text_token match + esc, :char
  350. else
  351. encoder.text_token match + esc, :content
  352. end
  353. end
  354. when '#'
  355. case peek(1)
  356. when '{'
  357. inline_block_stack ||= []
  358. inline_block_stack << [state, inline_block_curly_depth, heredocs]
  359. value_expected = true
  360. state = :initial
  361. inline_block_curly_depth = 1
  362. encoder.begin_group :inline
  363. encoder.text_token match + getch, :inline_delimiter
  364. when '$', '@'
  365. encoder.text_token match, :escape
  366. last_state = state
  367. state = :initial
  368. else
  369. skipped #:nocov:
  370. skipped raise_inspect 'else-case # reached; #%p not handled' % [peek(1)], encoder
  371. skipped #:nocov:
  372. end
  373. when state.opening_paren
  374. state.paren_depth += 1
  375. encoder.text_token match, :content
  376. else
  377. #:nocov
  378. raise_inspect 'else-case " reached; %p not handled, state = %p' % [match, state], encoder
  379. skipped #:nocov:
  380. skipped
  381. skipped end
  382. skipped
  383. skipped end
  384. skipped
  385. skipped end
  386. skipped
  387. skipped # cleaning up
  388. skipped if state.is_a? StringState
  389. skipped encoder.end_group state.type
  390. skipped end
  391. skipped
  392. skipped if options[:keep_state]
  393. skipped if state.is_a?(StringState) && state.heredoc
  394. skipped (heredocs ||= []).unshift state
  395. skipped state = :initial
  396. skipped elsif heredocs && heredocs.empty?
  397. skipped heredocs = nil
  398. skipped end
  399. skipped @state = state, heredocs
  400. skipped end
  401. skipped
  402. skipped if inline_block_stack
  403. skipped until inline_block_stack.empty?
  404. skipped state, = *inline_block_stack.pop
  405. skipped encoder.end_group :inline
  406. skipped encoder.end_group state.type
  407. skipped end
  408. skipped end
  409. skipped
  410. skipped encoder
  411. skipped end
  412. skipped
  413. skipped end
  414. skipped
  415. skipped end
  416. skipped end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/patterns.rb

100.0% lines covered

45 relevant lines. 45 lines covered and 0 lines missed.
    
  1. # encoding: utf-8
  2. 1 module CodeRay
  3. 1 module Scanners
  4. 1 module Ruby::Patterns # :nodoc: all
  5. KEYWORDS = %w[
  6. 1 and def end in or unless begin
  7. defined? ensure module redo super until
  8. BEGIN break do next rescue then
  9. when END case else for retry
  10. while alias class elsif if not return
  11. undef yield
  12. ]
  13. # See http://murfy.de/ruby-constants.
  14. PREDEFINED_CONSTANTS = %w[
  15. 1 nil true false self
  16. DATA ARGV ARGF ENV
  17. FALSE TRUE NIL
  18. STDERR STDIN STDOUT
  19. TOPLEVEL_BINDING
  20. RUBY_COPYRIGHT RUBY_DESCRIPTION RUBY_ENGINE RUBY_PATCHLEVEL
  21. RUBY_PLATFORM RUBY_RELEASE_DATE RUBY_REVISION RUBY_VERSION
  22. __FILE__ __LINE__ __ENCODING__
  23. ]
  24. 1 IDENT_KIND = WordList.new(:ident).
  25. add(KEYWORDS, :keyword).
  26. add(PREDEFINED_CONSTANTS, :predefined_constant)
  27. 1 KEYWORD_NEW_STATE = WordList.new(:initial).
  28. add(%w[ def ], :def_expected).
  29. add(%w[ undef ], :undef_expected).
  30. add(%w[ alias ], :alias_expected).
  31. add(%w[ class module ], :module_expected)
  32. 1 IDENT = 'ä'[/[[:alpha:]]/] == 'ä' ? Regexp.new('[[:alpha:]_[^\0-\177]][[:alnum:]_[^\0-\177]]*') : /[^\W\d]\w*/
  33. 1 METHOD_NAME = / #{IDENT} [?!]? /ox
  34. 1 METHOD_NAME_OPERATOR = /
  35. \*\*? # multiplication and power
  36. | [-+~]@? # plus, minus, tilde with and without at sign
  37. | [\/%&|^`] # division, modulo or format strings, and, or, xor, system
  38. | \[\]=? # array getter and setter
  39. | << | >> # append or shift left, shift right
  40. | <=?>? | >=? # comparison, rocket operator
  41. | ===? | =~ # simple equality, case equality, match
  42. | ![~=@]? # negation with and without at sign, not-equal and not-match
  43. /ox
  44. 1 METHOD_SUFFIX = / (?: [?!] | = (?![~>]|=(?!>)) ) /x
  45. 1 METHOD_NAME_EX = / #{IDENT} #{METHOD_SUFFIX}? | #{METHOD_NAME_OPERATOR} /ox
  46. 1 METHOD_AFTER_DOT = / #{IDENT} [?!]? | #{METHOD_NAME_OPERATOR} /ox
  47. 1 INSTANCE_VARIABLE = / @ #{IDENT} /ox
  48. 1 CLASS_VARIABLE = / @@ #{IDENT} /ox
  49. 1 OBJECT_VARIABLE = / @@? #{IDENT} /ox
  50. 1 GLOBAL_VARIABLE = / \$ (?: #{IDENT} | [1-9]\d* | 0\w* | [~&+`'=\/,;_.<>!@$?*":\\] | -[a-zA-Z_0-9] ) /ox
  51. 1 PREFIX_VARIABLE = / #{GLOBAL_VARIABLE} | #{OBJECT_VARIABLE} /ox
  52. 1 VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox
  53. QUOTE_TO_TYPE = {
  54. 1 '`' => :shell,
  55. '/' => :regexp,
  56. }
  57. 1 QUOTE_TO_TYPE.default = :string
  58. 1 REGEXP_MODIFIERS = /[mousenix]*/
  59. 1 DECIMAL = /\d+(?:_\d+)*/
  60. 1 OCTAL = /0_?[0-7]+(?:_[0-7]+)*/
  61. 1 HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/
  62. 1 BINARY = /0b[01]+(?:_[01]+)*/
  63. 1 EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
  64. 1 FLOAT_SUFFIX = / #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? /ox
  65. 1 FLOAT_OR_INT = / #{DECIMAL} (?: #{FLOAT_SUFFIX} () )? /ox
  66. 1 NUMERIC = / (?: (?=0) (?: #{OCTAL} | #{HEXADECIMAL} | #{BINARY} ) | #{FLOAT_OR_INT} ) /ox
  67. 1 SYMBOL = /
  68. :
  69. (?:
  70. #{METHOD_NAME_EX}
  71. | #{PREFIX_VARIABLE}
  72. | ['"]
  73. )
  74. /ox
  75. 1 METHOD_NAME_OR_SYMBOL = / #{METHOD_NAME_EX} | #{SYMBOL} /ox
  76. 1 SIMPLE_ESCAPE = /
  77. [abefnrstv]
  78. | [0-7]{1,3}
  79. | x[0-9A-Fa-f]{1,2}
  80. | .
  81. /mx
  82. 1 CONTROL_META_ESCAPE = /
  83. (?: M-|C-|c )
  84. (?: \\ (?: M-|C-|c ) )*
  85. (?: [^\\] | \\ #{SIMPLE_ESCAPE} )?
  86. /mox
  87. 1 ESCAPE = /
  88. #{CONTROL_META_ESCAPE} | #{SIMPLE_ESCAPE}
  89. /mox
  90. 1 CHARACTER = /
  91. \?
  92. (?:
  93. [^\s\\]
  94. | \\ #{ESCAPE}
  95. )
  96. /mox
  97. # NOTE: This is not completely correct, but
  98. # nobody needs heredoc delimiters ending with \n.
  99. 1 HEREDOC_OPEN = /
  100. << ([-~])? # $1 = float
  101. (?:
  102. ( [A-Za-z_0-9]+ ) # $2 = delim
  103. |
  104. ( ["'`\/] ) # $3 = quote, type
  105. ( [^\n]*? ) \3 # $4 = delim
  106. )
  107. /mx
  108. 1 RUBYDOC = /
  109. =begin (?!\S)
  110. .*?
  111. (?: \Z | ^=end (?!\S) [^\n]* )
  112. /mx
  113. 1 DATA = /
  114. __END__$
  115. .*?
  116. (?: \Z | (?=^\#CODE) )
  117. /mx
  118. 1 RUBYDOC_OR_DATA = / #{RUBYDOC} | #{DATA} /xo
  119. # Checks for a valid value to follow. This enables
  120. # value_expected in method calls without parentheses.
  121. 1 VALUE_FOLLOWS = /
  122. (?>[ \t\f\v]+)
  123. (?:
  124. [%\/][^\s=]
  125. | <<-?\S
  126. | [-+] \d
  127. | #{CHARACTER}
  128. )
  129. /ox
  130. 1 KEYWORDS_EXPECTING_VALUE = WordList.new.add(%w[
  131. and end in or unless begin
  132. defined? ensure redo super until
  133. break do next rescue then
  134. when case else for retry
  135. while elsif if not return
  136. yield
  137. ])
  138. 1 FANCY_STRING_START = / % ( [iIqQrswWx] | (?![a-zA-Z0-9]) ) ([^a-zA-Z0-9]) /x
  139. 1 FANCY_STRING_KIND = Hash.new(:string).merge({
  140. 'i' => :symbol,
  141. 'I' => :symbol,
  142. 'r' => :regexp,
  143. 's' => :symbol,
  144. 'x' => :shell,
  145. })
  146. 1 FANCY_STRING_INTERPRETED = Hash.new(true).merge({
  147. 'i' => false,
  148. 'q' => false,
  149. 's' => false,
  150. 'w' => false,
  151. })
  152. end
  153. end
  154. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/ruby/string_state.rb

27.78% lines covered

36 relevant lines. 10 lines covered and 26 lines missed.
    
  1. # encoding: utf-8
  2. 1 module CodeRay
  3. 1 module Scanners
  4. 1 class Ruby
  5. 1 class StringState < Struct.new :type, :interpreted, :delim, :heredoc,
  6. :opening_paren, :paren_depth, :pattern, :next_state # :nodoc: all
  7. 1 CLOSING_PAREN = Hash[ *%w[
  8. ( )
  9. [ ]
  10. < >
  11. { }
  12. 4 ] ].each { |k,v| k.freeze; v.freeze } # debug, if I try to change it with <<
  13. 1 STRING_PATTERN = Hash.new do |h, k|
  14. delim, interpreted = *k
  15. delim_pattern = Regexp.escape(delim)
  16. if closing_paren = CLOSING_PAREN[delim]
  17. delim_pattern << Regexp.escape(closing_paren)
  18. end
  19. delim_pattern << '\\\\' unless delim == '\\'
  20. # special_escapes =
  21. # case interpreted
  22. # when :regexp_symbols
  23. # '| [|?*+(){}\[\].^$]'
  24. # end
  25. if interpreted && delim != '#'
  26. / (?= [#{delim_pattern}] | \# [{$@] ) /mx
  27. else
  28. / (?= [#{delim_pattern}] ) /mx
  29. end.tap do |pattern|
  30. h[k] = pattern if (delim.respond_to?(:ord) ? delim.ord : delim[0]) < 256
  31. end
  32. end
  33. 1 def self.simple_key_pattern delim
  34. if delim == "'"
  35. / (?> (?: [^\\']+ | \\. )* ) ' : /mx
  36. else
  37. / (?> (?: [^\\"\#]+ | \\. | \#\$[\\"] | \#\{[^\{\}]+\} | \#(?!\{) )* ) " : /mx
  38. end
  39. end
  40. 1 def initialize kind, interpreted, delim, heredoc = false
  41. if heredoc
  42. pattern = heredoc_pattern delim, interpreted, heredoc == :indented
  43. delim = nil
  44. else
  45. pattern = STRING_PATTERN[ [delim, interpreted] ]
  46. if closing_paren = CLOSING_PAREN[delim]
  47. opening_paren = delim
  48. delim = closing_paren
  49. paren_depth = 1
  50. end
  51. end
  52. super kind, interpreted, delim, heredoc, opening_paren, paren_depth, pattern, :initial
  53. end
  54. 1 def heredoc_pattern delim, interpreted, indented
  55. # delim = delim.dup # workaround for old Ruby
  56. delim_pattern = Regexp.escape(delim)
  57. delim_pattern = / (?:\A|\n) #{ '(?>[ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x
  58. if interpreted
  59. / (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx # $1 set == end of heredoc
  60. else
  61. / (?= #{delim_pattern}() | \\ ) /mx
  62. end
  63. end
  64. end
  65. end
  66. end
  67. end

vender/bundle/ruby/2.6.0/gems/coderay-1.1.3/lib/coderay/scanners/scanner.rb

69.37% lines covered

111 relevant lines. 77 lines covered and 34 lines missed.
    
  1. # encoding: utf-8
  2. 1 module CodeRay
  3. 1 module Scanners
  4. # = Scanner
  5. #
  6. # The base class for all Scanners.
  7. #
  8. # It is a subclass of Ruby's great +StringScanner+, which
  9. # makes it easy to access the scanning methods inside.
  10. #
  11. # It is also +Enumerable+, so you can use it like an Array of
  12. # Tokens:
  13. #
  14. # require 'coderay'
  15. #
  16. # c_scanner = CodeRay::Scanners[:c].new "if (*p == '{') nest++;"
  17. #
  18. # for text, kind in c_scanner
  19. # puts text if kind == :operator
  20. # end
  21. #
  22. # # prints: (*==)++;
  23. #
  24. # OK, this is a very simple example :)
  25. # You can also use +map+, +any?+, +find+ and even +sort_by+,
  26. # if you want.
  27. 1 class Scanner < StringScanner
  28. 1 extend Plugin
  29. 1 plugin_host Scanners
  30. # Raised if a Scanner fails while scanning
  31. 1 ScanError = Class.new StandardError
  32. # The default options for all scanner classes.
  33. #
  34. # Define @default_options for subclasses.
  35. 1 DEFAULT_OPTIONS = { }
  36. 1 KINDS_NOT_LOC = [:comment, :doctype, :docstring]
  37. 1 attr_accessor :state
  38. 1 class << self
  39. # Normalizes the given code into a string with UNIX newlines, in the
  40. # scanner's internal encoding, with invalid and undefined charachters
  41. # replaced by placeholders. Always returns a new object.
  42. 1 def normalize code
  43. # original = code
  44. 6 code = code.to_s unless code.is_a? ::String
  45. 6 return code if code.empty?
  46. 6 if code.respond_to? :encoding
  47. 6 code = encode_with_encoding code, self.encoding
  48. else
  49. code = to_unix code
  50. end
  51. # code = code.dup if code.eql? original
  52. 6 code
  53. end
  54. # The typical filename suffix for this scanner's language.
  55. 1 def file_extension extension = lang
  56. 1 @file_extension ||= extension.to_s
  57. end
  58. # The encoding used internally by this scanner.
  59. 1 def encoding name = 'UTF-8'
  60. 6 @encoding ||= defined?(Encoding.find) && Encoding.find(name)
  61. end
  62. # The lang of this Scanner class, which is equal to its Plugin ID.
  63. 1 def lang
  64. @plugin_id
  65. end
  66. 1 protected
  67. 1 def encode_with_encoding code, target_encoding
  68. 6 if code.encoding == target_encoding
  69. 6 if code.valid_encoding?
  70. 6 return to_unix(code)
  71. else
  72. source_encoding = guess_encoding code
  73. end
  74. else
  75. source_encoding = code.encoding
  76. end
  77. # print "encode_with_encoding from #{source_encoding} to #{target_encoding}"
  78. code.encode target_encoding, source_encoding, :universal_newline => true, :undef => :replace, :invalid => :replace
  79. end
  80. 1 def to_unix code
  81. 6 code.index(?\r) ? code.gsub(/\r\n?/, "\n") : code
  82. end
  83. 1 def guess_encoding s
  84. skipped #:nocov:
  85. skipped IO.popen("file -b --mime -", "w+") do |file|
  86. skipped file.write s[0, 1024]
  87. skipped file.close_write
  88. skipped begin
  89. skipped Encoding.find file.gets[/charset=([-\w]+)/, 1]
  90. skipped rescue ArgumentError
  91. skipped Encoding::BINARY
  92. skipped end
  93. skipped end
  94. skipped #:nocov:
  95. end
  96. end
  97. # Create a new Scanner.
  98. #
  99. # * +code+ is the input String and is handled by the superclass
  100. # StringScanner.
  101. # * +options+ is a Hash with Symbols as keys.
  102. # It is merged with the default options of the class (you can
  103. # overwrite default options here.)
  104. #
  105. # Else, a Tokens object is used.
  106. 1 def initialize code = '', options = {}
  107. 6 if self.class == Scanner
  108. raise NotImplementedError, "I am only the basic Scanner class. I can't scan anything. :( Use my subclasses."
  109. end
  110. 6 @options = self.class::DEFAULT_OPTIONS.merge options
  111. 6 super self.class.normalize(code)
  112. 6 @tokens = options[:tokens] || Tokens.new
  113. 6 @tokens.scanner = self if @tokens.respond_to? :scanner=
  114. 6 setup
  115. end
  116. # Sets back the scanner. Subclasses should redefine the reset_instance
  117. # method instead of this one.
  118. 1 def reset
  119. 6 super
  120. 6 reset_instance
  121. end
  122. # Set a new string to be scanned.
  123. 1 def string= code
  124. code = self.class.normalize(code)
  125. super code
  126. reset_instance
  127. end
  128. # the Plugin ID for this scanner
  129. 1 def lang
  130. self.class.lang
  131. end
  132. # the default file extension for this scanner
  133. 1 def file_extension
  134. self.class.file_extension
  135. end
  136. # Scan the code and returns all tokens in a Tokens object.
  137. 1 def tokenize source = nil, options = {}
  138. 6 options = @options.merge(options)
  139. 6 set_tokens_from_options options
  140. 6 set_string_from_source source
  141. begin
  142. 6 scan_tokens @tokens, options
  143. rescue => e
  144. message = "Error in %s#scan_tokens, initial state was: %p" % [self.class, defined?(state) && state]
  145. raise_inspect e.message, @tokens, message, 30, e.backtrace
  146. end
  147. 6 @cached_tokens = @tokens
  148. 6 if source.is_a? Array
  149. @tokens.split_into_parts(*source.map { |part| part.size })
  150. else
  151. 6 @tokens
  152. end
  153. end
  154. # Cache the result of tokenize.
  155. 1 def tokens
  156. @cached_tokens ||= tokenize
  157. end
  158. # Traverse the tokens.
  159. 1 def each &block
  160. tokens.each(&block)
  161. end
  162. 1 include Enumerable
  163. # The current line position of the scanner, starting with 1.
  164. # See also: #column.
  165. #
  166. # Beware, this is implemented inefficiently. It should be used
  167. # for debugging only.
  168. 1 def line pos = self.pos
  169. return 1 if pos <= 0
  170. binary_string[0...pos].count("\n") + 1
  171. end
  172. # The current column position of the scanner, starting with 1.
  173. # See also: #line.
  174. 1 def column pos = self.pos
  175. return 1 if pos <= 0
  176. pos - (binary_string.rindex(?\n, pos - 1) || -1)
  177. end
  178. # The string in binary encoding.
  179. #
  180. # To be used with #pos, which is the index of the byte the scanner
  181. # will scan next.
  182. 1 def binary_string
  183. @binary_string ||=
  184. if string.respond_to?(:bytesize) && string.bytesize != string.size
  185. skipped #:nocov:
  186. skipped string.dup.force_encoding('binary')
  187. skipped #:nocov:
  188. else
  189. string
  190. end
  191. end
  192. 1 protected
  193. # Can be implemented by subclasses to do some initialization
  194. # that has to be done once per instance.
  195. #
  196. # Use reset for initialization that has to be done once per
  197. # scan.
  198. 1 def setup # :doc:
  199. end
  200. 1 def set_string_from_source source
  201. 6 case source
  202. when Array
  203. self.string = self.class.normalize(source.join)
  204. when nil
  205. 6 reset
  206. else
  207. self.string = self.class.normalize(source)
  208. end
  209. end
  210. 1 def set_tokens_from_options options
  211. 6 @tokens = options[:tokens] || @tokens || Tokens.new
  212. 6 @tokens.scanner = self if @tokens.respond_to? :scanner=
  213. end
  214. # This is the central method, and commonly the only one a
  215. # subclass implements.
  216. #
  217. # Subclasses must implement this method; it must return +tokens+
  218. # and must only use Tokens#<< for storing scanned tokens!
  219. 1 def scan_tokens tokens, options # :doc:
  220. raise NotImplementedError, "#{self.class}#scan_tokens not implemented."
  221. end
  222. # Resets the scanner.
  223. 1 def reset_instance
  224. 6 @tokens.clear if @tokens.respond_to?(:clear) && !@options[:keep_tokens]
  225. 6 @cached_tokens = nil
  226. 6 @binary_string = nil if defined? @binary_string
  227. end
  228. 1 SCAN_ERROR_MESSAGE = <<-MESSAGE
  229. ***ERROR in %s: %s (after %s tokens)
  230. tokens:
  231. %s
  232. %s
  233. surrounding code:
  234. %p ~~ %p
  235. ***ERROR***
  236. MESSAGE
  237. 1 def raise_inspect_arguments message, tokens, state, ambit
  238. return File.basename(caller[0]),
  239. message,
  240. tokens_size(tokens),
  241. tokens_last(tokens, 10).map(&:inspect).join("\n"),
  242. scanner_state_info(state),
  243. binary_string[pos - ambit, ambit],
  244. binary_string[pos, ambit]
  245. end
  246. 1 SCANNER_STATE_INFO = <<-INFO
  247. current line: %d column: %d pos: %d
  248. matched: %p state: %p
  249. bol?: %p, eos?: %p
  250. INFO
  251. 1 def scanner_state_info state
  252. SCANNER_STATE_INFO % [
  253. line, column, pos,
  254. matched, state || 'No state given!',
  255. bol?, eos?,
  256. ]
  257. end
  258. # Scanner error with additional status information
  259. 1 def raise_inspect message, tokens, state = self.state, ambit = 30, backtrace = caller
  260. raise ScanError, SCAN_ERROR_MESSAGE % raise_inspect_arguments(message, tokens, state, ambit), backtrace
  261. end
  262. 1 def tokens_size tokens
  263. tokens.size if tokens.respond_to?(:size)
  264. end
  265. 1 def tokens_last tokens, n
  266. tokens.respond_to?(:last) ? tokens.last(n) : []
  267. end
  268. # Shorthand for scan_until(/\z/).
  269. # This method also avoids a JRuby 1.9 mode bug.
  270. 1 def scan_rest
  271. rest = self.rest
  272. terminate
  273. rest
  274. end
  275. end
  276. end
  277. end

vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs.rb

23.85% lines covered

260 relevant lines. 62 lines covered and 198 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Diff; end unless defined? Diff # rubocop:disable Style/Documentation
  3. # == How Diff Works (by Mark-Jason Dominus)
  4. #
  5. # I once read an article written by the authors of +diff+; they said that they
  6. # hard worked very hard on the algorithm until they found the right one.
  7. #
  8. # I think what they ended up using (and I hope someone will correct me, because
  9. # I am not very confident about this) was the `longest common subsequence'
  10. # method. In the LCS problem, you have two sequences of items:
  11. #
  12. # a b c d f g h j q z
  13. # a b c d e f g i j k r x y z
  14. #
  15. # and you want to find the longest sequence of items that is present in both
  16. # original sequences in the same order. That is, you want to find a new
  17. # sequence *S* which can be obtained from the first sequence by deleting some
  18. # items, and from the second sequence by deleting other items. You also want
  19. # *S* to be as long as possible. In this case *S* is:
  20. #
  21. # a b c d f g j z
  22. #
  23. # From there it's only a small step to get diff-like output:
  24. #
  25. # e h i k q r x y
  26. # + - + + - + + +
  27. #
  28. # This module solves the LCS problem. It also includes a canned function to
  29. # generate +diff+-like output.
  30. #
  31. # It might seem from the example above that the LCS of two sequences is always
  32. # pretty obvious, but that's not always the case, especially when the two
  33. # sequences have many repeated elements. For example, consider
  34. #
  35. # a x b y c z p d q
  36. # a b c a x b y c z
  37. #
  38. # A naive approach might start by matching up the +a+ and +b+ that appear at
  39. # the beginning of each sequence, like this:
  40. #
  41. # a x b y c z p d q
  42. # a b c a b y c z
  43. #
  44. # This finds the common subsequence +a b c z+. But actually, the LCS is +a x b
  45. # y c z+:
  46. #
  47. # a x b y c z p d q
  48. # a b c a x b y c z
  49. 1 module Diff::LCS
  50. 1 VERSION = '1.4.4'
  51. end
  52. 1 require 'diff/lcs/callbacks'
  53. 1 require 'diff/lcs/internals'
  54. 1 module Diff::LCS # rubocop:disable Style/Documentation
  55. # Returns an Array containing the longest common subsequence(s) between
  56. # +self+ and +other+. See Diff::LCS#lcs.
  57. #
  58. # lcs = seq1.lcs(seq2)
  59. #
  60. # A note when using objects: Diff::LCS only works properly when each object
  61. # can be used as a key in a Hash, which typically means that the objects must
  62. # implement Object#eql? in a way that two identical values compare
  63. # identically for key purposes. That is:
  64. #
  65. # O.new('a').eql?(O.new('a')) == true
  66. 1 def lcs(other, &block) #:yields self[i] if there are matched subsequences:
  67. Diff::LCS.lcs(self, other, &block)
  68. end
  69. # Returns the difference set between +self+ and +other+. See Diff::LCS#diff.
  70. 1 def diff(other, callbacks = nil, &block)
  71. Diff::LCS.diff(self, other, callbacks, &block)
  72. end
  73. # Returns the balanced ("side-by-side") difference set between +self+ and
  74. # +other+. See Diff::LCS#sdiff.
  75. 1 def sdiff(other, callbacks = nil, &block)
  76. Diff::LCS.sdiff(self, other, callbacks, &block)
  77. end
  78. # Traverses the discovered longest common subsequences between +self+ and
  79. # +other+. See Diff::LCS#traverse_sequences.
  80. 1 def traverse_sequences(other, callbacks = nil, &block)
  81. traverse_sequences(self, other, callbacks || Diff::LCS::SequenceCallbacks, &block)
  82. end
  83. # Traverses the discovered longest common subsequences between +self+ and
  84. # +other+ using the alternate, balanced algorithm. See
  85. # Diff::LCS#traverse_balanced.
  86. 1 def traverse_balanced(other, callbacks = nil, &block)
  87. traverse_balanced(self, other, callbacks || Diff::LCS::BalancedCallbacks, &block)
  88. end
  89. # Attempts to patch +self+ with the provided +patchset+. A new sequence based
  90. # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Attempts
  91. # to autodiscover the direction of the patch.
  92. 1 def patch(patchset)
  93. Diff::LCS.patch(self, patchset)
  94. end
  95. 1 alias unpatch patch
  96. # Attempts to patch +self+ with the provided +patchset+. A new sequence based
  97. # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Does no
  98. # patch direction autodiscovery.
  99. 1 def patch!(patchset)
  100. Diff::LCS.patch!(self, patchset)
  101. end
  102. # Attempts to unpatch +self+ with the provided +patchset+. A new sequence
  103. # based on +self+ and the +patchset+ will be created. See Diff::LCS#unpatch.
  104. # Does no patch direction autodiscovery.
  105. 1 def unpatch!(patchset)
  106. Diff::LCS.unpatch!(self, patchset)
  107. end
  108. # Attempts to patch +self+ with the provided +patchset+, using #patch!. If
  109. # the sequence this is used on supports #replace, the value of +self+ will be
  110. # replaced. See Diff::LCS#patch. Does no patch direction autodiscovery.
  111. 1 def patch_me(patchset)
  112. if respond_to? :replace
  113. replace(patch!(patchset))
  114. else
  115. patch!(patchset)
  116. end
  117. end
  118. # Attempts to unpatch +self+ with the provided +patchset+, using #unpatch!.
  119. # If the sequence this is used on supports #replace, the value of +self+ will
  120. # be replaced. See Diff::LCS#unpatch. Does no patch direction autodiscovery.
  121. 1 def unpatch_me(patchset)
  122. if respond_to? :replace
  123. replace(unpatch!(patchset))
  124. else
  125. unpatch!(patchset)
  126. end
  127. end
  128. end
  129. 1 class << Diff::LCS
  130. 1 def lcs(seq1, seq2, &block) #:yields seq1[i] for each matched:
  131. matches = Diff::LCS::Internals.lcs(seq1, seq2)
  132. ret = []
  133. string = seq1.kind_of? String
  134. matches.each_with_index do |_e, i|
  135. next if matches[i].nil?
  136. v = string ? seq1[i, 1] : seq1[i]
  137. v = block[v] if block
  138. ret << v
  139. end
  140. ret
  141. end
  142. 1 alias LCS lcs
  143. # #diff computes the smallest set of additions and deletions necessary to
  144. # turn the first sequence into the second, and returns a description of these
  145. # changes.
  146. #
  147. # See Diff::LCS::DiffCallbacks for the default behaviour. An alternate
  148. # behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a
  149. # Class argument is provided for +callbacks+, #diff will attempt to
  150. # initialise it. If the +callbacks+ object (possibly initialised) responds to
  151. # #finish, it will be called.
  152. 1 def diff(seq1, seq2, callbacks = nil, &block) # :yields diff changes:
  153. 1 diff_traversal(:diff, seq1, seq2, callbacks || Diff::LCS::DiffCallbacks, &block)
  154. end
  155. # #sdiff computes all necessary components to show two sequences and their
  156. # minimized differences side by side, just like the Unix utility
  157. # <em>sdiff</em> does:
  158. #
  159. # old < -
  160. # same same
  161. # before | after
  162. # - > new
  163. #
  164. # See Diff::LCS::SDiffCallbacks for the default behaviour. An alternate
  165. # behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a
  166. # Class argument is provided for +callbacks+, #diff will attempt to
  167. # initialise it. If the +callbacks+ object (possibly initialised) responds to
  168. # #finish, it will be called.
  169. #
  170. # Each element of a returned array is a Diff::LCS::ContextChange object,
  171. # which can be implicitly converted to an array.
  172. #
  173. # Diff::LCS.sdiff(a, b).each do |action, (old_pos, old_element), (new_pos, new_element)|
  174. # case action
  175. # when '!'
  176. # # replace
  177. # when '-'
  178. # # delete
  179. # when '+'
  180. # # insert
  181. # end
  182. # end
  183. 1 def sdiff(seq1, seq2, callbacks = nil, &block) #:yields diff changes:
  184. diff_traversal(:sdiff, seq1, seq2, callbacks || Diff::LCS::SDiffCallbacks, &block)
  185. end
  186. # #traverse_sequences is the most general facility provided by this module;
  187. # #diff and #lcs are implemented as calls to it.
  188. #
  189. # The arguments to #traverse_sequences are the two sequences to traverse, and
  190. # a callback object, like this:
  191. #
  192. # traverse_sequences(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
  193. #
  194. # == Callback Methods
  195. #
  196. # Optional callback methods are <em>emphasized</em>.
  197. #
  198. # callbacks#match:: Called when +a+ and +b+ are pointing to
  199. # common elements in +A+ and +B+.
  200. # callbacks#discard_a:: Called when +a+ is pointing to an
  201. # element not in +B+.
  202. # callbacks#discard_b:: Called when +b+ is pointing to an
  203. # element not in +A+.
  204. # <em>callbacks#finished_a</em>:: Called when +a+ has reached the end of
  205. # sequence +A+.
  206. # <em>callbacks#finished_b</em>:: Called when +b+ has reached the end of
  207. # sequence +B+.
  208. #
  209. # == Algorithm
  210. #
  211. # a---+
  212. # v
  213. # A = a b c e h j l m n p
  214. # B = b c d e f j k l m r s t
  215. # ^
  216. # b---+
  217. #
  218. # If there are two arrows (+a+ and +b+) pointing to elements of sequences +A+
  219. # and +B+, the arrows will initially point to the first elements of their
  220. # respective sequences. #traverse_sequences will advance the arrows through
  221. # the sequences one element at a time, calling a method on the user-specified
  222. # callback object before each advance. It will advance the arrows in such a
  223. # way that if there are elements <tt>A[i]</tt> and <tt>B[j]</tt> which are
  224. # both equal and part of the longest common subsequence, there will be some
  225. # moment during the execution of #traverse_sequences when arrow +a+ is
  226. # pointing to <tt>A[i]</tt> and arrow +b+ is pointing to <tt>B[j]</tt>. When
  227. # this happens, #traverse_sequences will call <tt>callbacks#match</tt> and
  228. # then it will advance both arrows.
  229. #
  230. # Otherwise, one of the arrows is pointing to an element of its sequence that
  231. # is not part of the longest common subsequence. #traverse_sequences will
  232. # advance that arrow and will call <tt>callbacks#discard_a</tt> or
  233. # <tt>callbacks#discard_b</tt>, depending on which arrow it advanced. If both
  234. # arrows point to elements that are not part of the longest common
  235. # subsequence, then #traverse_sequences will advance one of them and call the
  236. # appropriate callback, but it is not specified which it will call.
  237. #
  238. # The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>, and
  239. # <tt>callbacks#discard_b</tt> are invoked with an event comprising the
  240. # action ("=", "+", or "-", respectively), the indicies +i+ and +j+, and the
  241. # elements <tt>A[i]</tt> and <tt>B[j]</tt>. Return values are discarded by
  242. # #traverse_sequences.
  243. #
  244. # === End of Sequences
  245. #
  246. # If arrow +a+ reaches the end of its sequence before arrow +b+ does,
  247. # #traverse_sequence will try to call <tt>callbacks#finished_a</tt> with the
  248. # last index and element of +A+ (<tt>A[-1]</tt>) and the current index and
  249. # element of +B+ (<tt>B[j]</tt>). If <tt>callbacks#finished_a</tt> does not
  250. # exist, then <tt>callbacks#discard_b</tt> will be called on each element of
  251. # +B+ until the end of the sequence is reached (the call will be done with
  252. # <tt>A[-1]</tt> and <tt>B[j]</tt> for each element).
  253. #
  254. # If +b+ reaches the end of +B+ before +a+ reaches the end of +A+,
  255. # <tt>callbacks#finished_b</tt> will be called with the current index and
  256. # element of +A+ (<tt>A[i]</tt>) and the last index and element of +B+
  257. # (<tt>A[-1]</tt>). Again, if <tt>callbacks#finished_b</tt> does not exist on
  258. # the callback object, then <tt>callbacks#discard_a</tt> will be called on
  259. # each element of +A+ until the end of the sequence is reached (<tt>A[i]</tt>
  260. # and <tt>B[-1]</tt>).
  261. #
  262. # There is a chance that one additional <tt>callbacks#discard_a</tt> or
  263. # <tt>callbacks#discard_b</tt> will be called after the end of the sequence
  264. # is reached, if +a+ has not yet reached the end of +A+ or +b+ has not yet
  265. # reached the end of +B+.
  266. 1 def traverse_sequences(seq1, seq2, callbacks = Diff::LCS::SequenceCallbacks) #:yields change events:
  267. 1 callbacks ||= Diff::LCS::SequenceCallbacks
  268. 1 matches = Diff::LCS::Internals.lcs(seq1, seq2)
  269. 1 run_finished_a = run_finished_b = false
  270. 1 string = seq1.kind_of?(String)
  271. 1 a_size = seq1.size
  272. 1 b_size = seq2.size
  273. 1 ai = bj = 0
  274. 1 (0..matches.size).each do |i|
  275. 1 b_line = matches[i]
  276. 1 ax = string ? seq1[i, 1] : seq1[i]
  277. 1 bx = string ? seq2[bj, 1] : seq2[bj]
  278. 1 if b_line.nil?
  279. 1 unless ax.nil? or (string and ax.empty?)
  280. 1 event = Diff::LCS::ContextChange.new('-', i, ax, bj, bx)
  281. 1 event = yield event if block_given?
  282. 1 callbacks.discard_a(event)
  283. end
  284. else
  285. loop do
  286. break unless bj < b_line
  287. bx = string ? seq2[bj, 1] : seq2[bj]
  288. event = Diff::LCS::ContextChange.new('+', i, ax, bj, bx)
  289. event = yield event if block_given?
  290. callbacks.discard_b(event)
  291. bj += 1
  292. end
  293. bx = string ? seq2[bj, 1] : seq2[bj]
  294. event = Diff::LCS::ContextChange.new('=', i, ax, bj, bx)
  295. event = yield event if block_given?
  296. callbacks.match(event)
  297. bj += 1
  298. end
  299. 1 ai = i
  300. end
  301. 1 ai += 1
  302. # The last entry (if any) processed was a match. +ai+ and +bj+ point just
  303. # past the last matching lines in their sequences.
  304. 3 while (ai < a_size) or (bj < b_size)
  305. # last A?
  306. 1 if ai == a_size and bj < b_size
  307. 1 if callbacks.respond_to?(:finished_a) and !run_finished_a
  308. ax = string ? seq1[-1, 1] : seq1[-1]
  309. bx = string ? seq2[bj, 1] : seq2[bj]
  310. event = Diff::LCS::ContextChange.new('>', (a_size - 1), ax, bj, bx)
  311. event = yield event if block_given?
  312. callbacks.finished_a(event)
  313. run_finished_a = true
  314. else
  315. 1 ax = string ? seq1[ai, 1] : seq1[ai]
  316. 1 loop do
  317. 1 bx = string ? seq2[bj, 1] : seq2[bj]
  318. 1 event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
  319. 1 event = yield event if block_given?
  320. 1 callbacks.discard_b(event)
  321. 1 bj += 1
  322. 1 break unless bj < b_size
  323. end
  324. end
  325. end
  326. # last B?
  327. 1 if bj == b_size and ai < a_size
  328. if callbacks.respond_to?(:finished_b) and !run_finished_b
  329. ax = string ? seq1[ai, 1] : seq1[ai]
  330. bx = string ? seq2[-1, 1] : seq2[-1]
  331. event = Diff::LCS::ContextChange.new('<', ai, ax, (b_size - 1), bx)
  332. event = yield event if block_given?
  333. callbacks.finished_b(event)
  334. run_finished_b = true
  335. else
  336. bx = string ? seq2[bj, 1] : seq2[bj]
  337. loop do
  338. ax = string ? seq1[ai, 1] : seq1[ai]
  339. event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
  340. event = yield event if block_given?
  341. callbacks.discard_a(event)
  342. ai += 1
  343. break unless bj < b_size
  344. end
  345. end
  346. end
  347. 1 if ai < a_size
  348. ax = string ? seq1[ai, 1] : seq1[ai]
  349. bx = string ? seq2[bj, 1] : seq2[bj]
  350. event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
  351. event = yield event if block_given?
  352. callbacks.discard_a(event)
  353. ai += 1
  354. end
  355. 1 next unless bj < b_size
  356. ax = string ? seq1[ai, 1] : seq1[ai]
  357. bx = string ? seq2[bj, 1] : seq2[bj]
  358. event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
  359. event = yield event if block_given?
  360. callbacks.discard_b(event)
  361. bj += 1
  362. end
  363. end
  364. # #traverse_balanced is an alternative to #traverse_sequences. It uses a
  365. # different algorithm to iterate through the entries in the computed longest
  366. # common subsequence. Instead of viewing the changes as insertions or
  367. # deletions from one of the sequences, #traverse_balanced will report
  368. # <em>changes</em> between the sequences.
  369. #
  370. # The arguments to #traverse_balanced are the two sequences to traverse and a
  371. # callback object, like this:
  372. #
  373. # traverse_balanced(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
  374. #
  375. # #sdiff is implemented with #traverse_balanced.
  376. #
  377. # == Callback Methods
  378. #
  379. # Optional callback methods are <em>emphasized</em>.
  380. #
  381. # callbacks#match:: Called when +a+ and +b+ are pointing to
  382. # common elements in +A+ and +B+.
  383. # callbacks#discard_a:: Called when +a+ is pointing to an
  384. # element not in +B+.
  385. # callbacks#discard_b:: Called when +b+ is pointing to an
  386. # element not in +A+.
  387. # <em>callbacks#change</em>:: Called when +a+ and +b+ are pointing to
  388. # the same relative position, but
  389. # <tt>A[a]</tt> and <tt>B[b]</tt> are not
  390. # the same; a <em>change</em> has
  391. # occurred.
  392. #
  393. # #traverse_balanced might be a bit slower than #traverse_sequences,
  394. # noticable only while processing huge amounts of data.
  395. #
  396. # == Algorithm
  397. #
  398. # a---+
  399. # v
  400. # A = a b c e h j l m n p
  401. # B = b c d e f j k l m r s t
  402. # ^
  403. # b---+
  404. #
  405. # === Matches
  406. #
  407. # If there are two arrows (+a+ and +b+) pointing to elements of sequences +A+
  408. # and +B+, the arrows will initially point to the first elements of their
  409. # respective sequences. #traverse_sequences will advance the arrows through
  410. # the sequences one element at a time, calling a method on the user-specified
  411. # callback object before each advance. It will advance the arrows in such a
  412. # way that if there are elements <tt>A[i]</tt> and <tt>B[j]</tt> which are
  413. # both equal and part of the longest common subsequence, there will be some
  414. # moment during the execution of #traverse_sequences when arrow +a+ is
  415. # pointing to <tt>A[i]</tt> and arrow +b+ is pointing to <tt>B[j]</tt>. When
  416. # this happens, #traverse_sequences will call <tt>callbacks#match</tt> and
  417. # then it will advance both arrows.
  418. #
  419. # === Discards
  420. #
  421. # Otherwise, one of the arrows is pointing to an element of its sequence that
  422. # is not part of the longest common subsequence. #traverse_sequences will
  423. # advance that arrow and will call <tt>callbacks#discard_a</tt> or
  424. # <tt>callbacks#discard_b</tt>, depending on which arrow it advanced.
  425. #
  426. # === Changes
  427. #
  428. # If both +a+ and +b+ point to elements that are not part of the longest
  429. # common subsequence, then #traverse_sequences will try to call
  430. # <tt>callbacks#change</tt> and advance both arrows. If
  431. # <tt>callbacks#change</tt> is not implemented, then
  432. # <tt>callbacks#discard_a</tt> and <tt>callbacks#discard_b</tt> will be
  433. # called in turn.
  434. #
  435. # The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
  436. # <tt>callbacks#discard_b</tt>, and <tt>callbacks#change</tt> are invoked
  437. # with an event comprising the action ("=", "+", "-", or "!", respectively),
  438. # the indicies +i+ and +j+, and the elements <tt>A[i]</tt> and <tt>B[j]</tt>.
  439. # Return values are discarded by #traverse_balanced.
  440. #
  441. # === Context
  442. #
  443. # Note that +i+ and +j+ may not be the same index position, even if +a+ and
  444. # +b+ are considered to be pointing to matching or changed elements.
  445. 1 def traverse_balanced(seq1, seq2, callbacks = Diff::LCS::BalancedCallbacks)
  446. matches = Diff::LCS::Internals.lcs(seq1, seq2)
  447. a_size = seq1.size
  448. b_size = seq2.size
  449. ai = bj = mb = 0
  450. ma = -1
  451. string = seq1.kind_of?(String)
  452. # Process all the lines in the match vector.
  453. loop do
  454. # Find next match indices +ma+ and +mb+
  455. loop do
  456. ma += 1
  457. break unless ma < matches.size and matches[ma].nil?
  458. end
  459. break if ma >= matches.size # end of matches?
  460. mb = matches[ma]
  461. # Change(seq2)
  462. while (ai < ma) or (bj < mb)
  463. ax = string ? seq1[ai, 1] : seq1[ai]
  464. bx = string ? seq2[bj, 1] : seq2[bj]
  465. case [(ai < ma), (bj < mb)]
  466. when [true, true]
  467. if callbacks.respond_to?(:change)
  468. event = Diff::LCS::ContextChange.new('!', ai, ax, bj, bx)
  469. event = yield event if block_given?
  470. callbacks.change(event)
  471. ai += 1
  472. else
  473. event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
  474. event = yield event if block_given?
  475. callbacks.discard_a(event)
  476. ai += 1
  477. ax = string ? seq1[ai, 1] : seq1[ai]
  478. event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
  479. event = yield event if block_given?
  480. callbacks.discard_b(event)
  481. end
  482. bj += 1
  483. when [true, false]
  484. event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
  485. event = yield event if block_given?
  486. callbacks.discard_a(event)
  487. ai += 1
  488. when [false, true]
  489. event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
  490. event = yield event if block_given?
  491. callbacks.discard_b(event)
  492. bj += 1
  493. end
  494. end
  495. # Match
  496. ax = string ? seq1[ai, 1] : seq1[ai]
  497. bx = string ? seq2[bj, 1] : seq2[bj]
  498. event = Diff::LCS::ContextChange.new('=', ai, ax, bj, bx)
  499. event = yield event if block_given?
  500. callbacks.match(event)
  501. ai += 1
  502. bj += 1
  503. end
  504. while (ai < a_size) or (bj < b_size)
  505. ax = string ? seq1[ai, 1] : seq1[ai]
  506. bx = string ? seq2[bj, 1] : seq2[bj]
  507. case [(ai < a_size), (bj < b_size)]
  508. when [true, true]
  509. if callbacks.respond_to?(:change)
  510. event = Diff::LCS::ContextChange.new('!', ai, ax, bj, bx)
  511. event = yield event if block_given?
  512. callbacks.change(event)
  513. ai += 1
  514. else
  515. event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
  516. event = yield event if block_given?
  517. callbacks.discard_a(event)
  518. ai += 1
  519. ax = string ? seq1[ai, 1] : seq1[ai]
  520. event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
  521. event = yield event if block_given?
  522. callbacks.discard_b(event)
  523. end
  524. bj += 1
  525. when [true, false]
  526. event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
  527. event = yield event if block_given?
  528. callbacks.discard_a(event)
  529. ai += 1
  530. when [false, true]
  531. event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
  532. event = yield event if block_given?
  533. callbacks.discard_b(event)
  534. bj += 1
  535. end
  536. end
  537. end
  538. PATCH_MAP = { #:nodoc:
  539. 1 :patch => { '+' => '+', '-' => '-', '!' => '!', '=' => '=' }.freeze,
  540. :unpatch => { '+' => '-', '-' => '+', '!' => '!', '=' => '=' }.freeze
  541. }.freeze
  542. # Applies a +patchset+ to the sequence +src+ according to the +direction+
  543. # (<tt>:patch</tt> or <tt>:unpatch</tt>), producing a new sequence.
  544. #
  545. # If the +direction+ is not specified, Diff::LCS::patch will attempt to
  546. # discover the direction of the +patchset+.
  547. #
  548. # A +patchset+ can be considered to apply forward (<tt>:patch</tt>) if the
  549. # following expression is true:
  550. #
  551. # patch(s1, diff(s1, s2)) -> s2
  552. #
  553. # A +patchset+ can be considered to apply backward (<tt>:unpatch</tt>) if the
  554. # following expression is true:
  555. #
  556. # patch(s2, diff(s1, s2)) -> s1
  557. #
  558. # If the +patchset+ contains no changes, the +src+ value will be returned as
  559. # either <tt>src.dup</tt> or +src+. A +patchset+ can be deemed as having no
  560. # changes if the following predicate returns true:
  561. #
  562. # patchset.empty? or
  563. # patchset.flatten(1).all? { |change| change.unchanged? }
  564. #
  565. # === Patchsets
  566. #
  567. # A +patchset+ is always an enumerable sequence of changes, hunks of changes,
  568. # or a mix of the two. A hunk of changes is an enumerable sequence of
  569. # changes:
  570. #
  571. # [ # patchset
  572. # # change
  573. # [ # hunk
  574. # # change
  575. # ]
  576. # ]
  577. #
  578. # The +patch+ method accepts <tt>patchset</tt>s that are enumerable sequences
  579. # containing either Diff::LCS::Change objects (or a subclass) or the array
  580. # representations of those objects. Prior to application, array
  581. # representations of Diff::LCS::Change objects will be reified.
  582. 1 def patch(src, patchset, direction = nil)
  583. # Normalize the patchset.
  584. has_changes, patchset = Diff::LCS::Internals.analyze_patchset(patchset)
  585. return src.respond_to?(:dup) ? src.dup : src unless has_changes
  586. string = src.kind_of?(String)
  587. # Start with a new empty type of the source's class
  588. res = src.class.new
  589. direction ||= Diff::LCS::Internals.intuit_diff_direction(src, patchset)
  590. ai = bj = 0
  591. patch_map = PATCH_MAP[direction]
  592. patchset.each do |change|
  593. # Both Change and ContextChange support #action
  594. action = patch_map[change.action]
  595. case change
  596. when Diff::LCS::ContextChange
  597. case direction
  598. when :patch
  599. el = change.new_element
  600. op = change.old_position
  601. np = change.new_position
  602. when :unpatch
  603. el = change.old_element
  604. op = change.new_position
  605. np = change.old_position
  606. end
  607. case action
  608. when '-' # Remove details from the old string
  609. while ai < op
  610. res << (string ? src[ai, 1] : src[ai])
  611. ai += 1
  612. bj += 1
  613. end
  614. ai += 1
  615. when '+'
  616. while bj < np
  617. res << (string ? src[ai, 1] : src[ai])
  618. ai += 1
  619. bj += 1
  620. end
  621. res << el
  622. bj += 1
  623. when '='
  624. # This only appears in sdiff output with the SDiff callback.
  625. # Therefore, we only need to worry about dealing with a single
  626. # element.
  627. res << el
  628. ai += 1
  629. bj += 1
  630. when '!'
  631. while ai < op
  632. res << (string ? src[ai, 1] : src[ai])
  633. ai += 1
  634. bj += 1
  635. end
  636. bj += 1
  637. ai += 1
  638. res << el
  639. end
  640. when Diff::LCS::Change
  641. case action
  642. when '-'
  643. while ai < change.position
  644. res << (string ? src[ai, 1] : src[ai])
  645. ai += 1
  646. bj += 1
  647. end
  648. ai += 1
  649. when '+'
  650. while bj < change.position
  651. res << (string ? src[ai, 1] : src[ai])
  652. ai += 1
  653. bj += 1
  654. end
  655. bj += 1
  656. res << change.element
  657. end
  658. end
  659. end
  660. while ai < src.size
  661. res << (string ? src[ai, 1] : src[ai])
  662. ai += 1
  663. bj += 1
  664. end
  665. res
  666. end
  667. # Given a set of patchset, convert the current version to the prior version.
  668. # Does no auto-discovery.
  669. 1 def unpatch!(src, patchset)
  670. patch(src, patchset, :unpatch)
  671. end
  672. # Given a set of patchset, convert the current version to the next version.
  673. # Does no auto-discovery.
  674. 1 def patch!(src, patchset)
  675. patch(src, patchset, :patch)
  676. end
  677. end
  678. 1 require 'diff/lcs/backports'

vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/backports.rb

25.0% lines covered

4 relevant lines. 1 lines covered and 3 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 unless 0.respond_to?(:positive?)
  3. class Fixnum # rubocop:disable Lint/UnifiedInteger, Style/Documentation
  4. def positive?
  5. self > 0 # rubocop:disable Style/NumericPredicate
  6. end
  7. end
  8. end

vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/block.rb

72.22% lines covered

18 relevant lines. 13 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. # A block is an operation removing, adding, or changing a group of items.
  3. # Basically, this is just a list of changes, where each change adds or
  4. # deletes a single item. Used by bin/ldiff.
  5. 1 class Diff::LCS::Block
  6. 1 attr_reader :changes, :insert, :remove
  7. 1 def initialize(chunk)
  8. 1 @changes = []
  9. 1 @insert = []
  10. 1 @remove = []
  11. 1 chunk.each do |item|
  12. 2 @changes << item
  13. 2 @remove << item if item.deleting?
  14. 2 @insert << item if item.adding?
  15. end
  16. end
  17. 1 def diff_size
  18. 2 @insert.size - @remove.size
  19. end
  20. 1 def op
  21. case [@remove.empty?, @insert.empty?]
  22. when [false, false]
  23. '!'
  24. when [false, true]
  25. '-'
  26. when [true, false]
  27. '+'
  28. else # [true, true]
  29. '^'
  30. end
  31. end
  32. end

vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/callbacks.rb

71.93% lines covered

57 relevant lines. 41 lines covered and 16 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require 'diff/lcs/change'
  3. 1 module Diff::LCS # rubocop:disable Style/Documentation
  4. # This callback object implements the default set of callback events,
  5. # which only returns the event itself. Note that #finished_a and
  6. # #finished_b are not implemented -- I haven't yet figured out where they
  7. # would be useful.
  8. #
  9. # Note that this is intended to be called as is, e.g.,
  10. #
  11. # Diff::LCS.LCS(seq1, seq2, Diff::LCS::DefaultCallbacks)
  12. 1 class DefaultCallbacks
  13. 1 class << self
  14. # Called when two items match.
  15. 1 def match(event)
  16. event
  17. end
  18. # Called when the old value is discarded in favour of the new value.
  19. 1 def discard_a(event)
  20. event
  21. end
  22. # Called when the new value is discarded in favour of the old value.
  23. 1 def discard_b(event)
  24. event
  25. end
  26. # Called when both the old and new values have changed.
  27. 1 def change(event)
  28. event
  29. end
  30. 1 private :new
  31. end
  32. end
  33. # An alias for DefaultCallbacks that is used in
  34. # Diff::LCS#traverse_sequences.
  35. #
  36. # Diff::LCS.LCS(seq1, seq2, Diff::LCS::SequenceCallbacks)
  37. 1 SequenceCallbacks = DefaultCallbacks
  38. # An alias for DefaultCallbacks that is used in
  39. # Diff::LCS#traverse_balanced.
  40. #
  41. # Diff::LCS.LCS(seq1, seq2, Diff::LCS::BalancedCallbacks)
  42. 1 BalancedCallbacks = DefaultCallbacks
  43. 1 def self.callbacks_for(callbacks)
  44. 1 callbacks.new rescue callbacks
  45. end
  46. end
  47. # This will produce a compound array of simple diff change objects. Each
  48. # element in the #diffs array is a +hunk+ or +hunk+ array, where each
  49. # element in each +hunk+ array is a single Change object representing the
  50. # addition or removal of a single element from one of the two tested
  51. # sequences. The +hunk+ provides the full context for the changes.
  52. #
  53. # diffs = Diff::LCS.diff(seq1, seq2)
  54. # # This example shows a simplified array format.
  55. # # [ [ [ '-', 0, 'a' ] ], # 1
  56. # # [ [ '+', 2, 'd' ] ], # 2
  57. # # [ [ '-', 4, 'h' ], # 3
  58. # # [ '+', 4, 'f' ] ],
  59. # # [ [ '+', 6, 'k' ] ], # 4
  60. # # [ [ '-', 8, 'n' ], # 5
  61. # # [ '-', 9, 'p' ],
  62. # # [ '+', 9, 'r' ],
  63. # # [ '+', 10, 's' ],
  64. # # [ '+', 11, 't' ] ] ]
  65. #
  66. # There are five hunks here. The first hunk says that the +a+ at position 0
  67. # of the first sequence should be deleted (<tt>'-'</tt>). The second hunk
  68. # says that the +d+ at position 2 of the second sequence should be inserted
  69. # (<tt>'+'</tt>). The third hunk says that the +h+ at position 4 of the
  70. # first sequence should be removed and replaced with the +f+ from position 4
  71. # of the second sequence. The other two hunks are described similarly.
  72. #
  73. # === Use
  74. #
  75. # This callback object must be initialised and is used by the Diff::LCS#diff
  76. # method.
  77. #
  78. # cbo = Diff::LCS::DiffCallbacks.new
  79. # Diff::LCS.LCS(seq1, seq2, cbo)
  80. # cbo.finish
  81. #
  82. # Note that the call to #finish is absolutely necessary, or the last set of
  83. # changes will not be visible. Alternatively, can be used as:
  84. #
  85. # cbo = Diff::LCS::DiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
  86. #
  87. # The necessary #finish call will be made.
  88. #
  89. # === Simplified Array Format
  90. #
  91. # The simplified array format used in the example above can be obtained
  92. # with:
  93. #
  94. # require 'pp'
  95. # pp diffs.map { |e| e.map { |f| f.to_a } }
  96. 1 class Diff::LCS::DiffCallbacks
  97. # Returns the difference set collected during the diff process.
  98. 1 attr_reader :diffs
  99. 1 def initialize # :yields self:
  100. 1 @hunk = []
  101. 1 @diffs = []
  102. 1 return unless block_given?
  103. begin
  104. yield self
  105. ensure
  106. finish
  107. end
  108. end
  109. # Finalizes the diff process. If an unprocessed hunk still exists, then it
  110. # is appended to the diff list.
  111. 1 def finish
  112. 1 finish_hunk
  113. end
  114. 1 def match(_event)
  115. finish_hunk
  116. end
  117. 1 def discard_a(event)
  118. 1 @hunk << Diff::LCS::Change.new('-', event.old_position, event.old_element)
  119. end
  120. 1 def discard_b(event)
  121. 1 @hunk << Diff::LCS::Change.new('+', event.new_position, event.new_element)
  122. end
  123. 1 def finish_hunk
  124. 1 @diffs << @hunk unless @hunk.empty?
  125. 1 @hunk = []
  126. end
  127. 1 private :finish_hunk
  128. end
  129. # This will produce a compound array of contextual diff change objects. Each
  130. # element in the #diffs array is a "hunk" array, where each element in each
  131. # "hunk" array is a single change. Each change is a Diff::LCS::ContextChange
  132. # that contains both the old index and new index values for the change. The
  133. # "hunk" provides the full context for the changes. Both old and new objects
  134. # will be presented for changed objects. +nil+ will be substituted for a
  135. # discarded object.
  136. #
  137. # seq1 = %w(a b c e h j l m n p)
  138. # seq2 = %w(b c d e f j k l m r s t)
  139. #
  140. # diffs = Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
  141. # # This example shows a simplified array format.
  142. # # [ [ [ '-', [ 0, 'a' ], [ 0, nil ] ] ], # 1
  143. # # [ [ '+', [ 3, nil ], [ 2, 'd' ] ] ], # 2
  144. # # [ [ '-', [ 4, 'h' ], [ 4, nil ] ], # 3
  145. # # [ '+', [ 5, nil ], [ 4, 'f' ] ] ],
  146. # # [ [ '+', [ 6, nil ], [ 6, 'k' ] ] ], # 4
  147. # # [ [ '-', [ 8, 'n' ], [ 9, nil ] ], # 5
  148. # # [ '+', [ 9, nil ], [ 9, 'r' ] ],
  149. # # [ '-', [ 9, 'p' ], [ 10, nil ] ],
  150. # # [ '+', [ 10, nil ], [ 10, 's' ] ],
  151. # # [ '+', [ 10, nil ], [ 11, 't' ] ] ] ]
  152. #
  153. # The five hunks shown are comprised of individual changes; if there is a
  154. # related set of changes, they are still shown individually.
  155. #
  156. # This callback can also be used with Diff::LCS#sdiff, which will produce
  157. # results like:
  158. #
  159. # diffs = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextCallbacks)
  160. # # This example shows a simplified array format.
  161. # # [ [ [ "-", [ 0, "a" ], [ 0, nil ] ] ], # 1
  162. # # [ [ "+", [ 3, nil ], [ 2, "d" ] ] ], # 2
  163. # # [ [ "!", [ 4, "h" ], [ 4, "f" ] ] ], # 3
  164. # # [ [ "+", [ 6, nil ], [ 6, "k" ] ] ], # 4
  165. # # [ [ "!", [ 8, "n" ], [ 9, "r" ] ], # 5
  166. # # [ "!", [ 9, "p" ], [ 10, "s" ] ],
  167. # # [ "+", [ 10, nil ], [ 11, "t" ] ] ] ]
  168. #
  169. # The five hunks are still present, but are significantly shorter in total
  170. # presentation, because changed items are shown as changes ("!") instead of
  171. # potentially "mismatched" pairs of additions and deletions.
  172. #
  173. # The result of this operation is similar to that of
  174. # Diff::LCS::SDiffCallbacks. They may be compared as:
  175. #
  176. # s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
  177. # c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten(1)
  178. #
  179. # s == c # -> true
  180. #
  181. # === Use
  182. #
  183. # This callback object must be initialised and can be used by the
  184. # Diff::LCS#diff or Diff::LCS#sdiff methods.
  185. #
  186. # cbo = Diff::LCS::ContextDiffCallbacks.new
  187. # Diff::LCS.LCS(seq1, seq2, cbo)
  188. # cbo.finish
  189. #
  190. # Note that the call to #finish is absolutely necessary, or the last set of
  191. # changes will not be visible. Alternatively, can be used as:
  192. #
  193. # cbo = Diff::LCS::ContextDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
  194. #
  195. # The necessary #finish call will be made.
  196. #
  197. # === Simplified Array Format
  198. #
  199. # The simplified array format used in the example above can be obtained
  200. # with:
  201. #
  202. # require 'pp'
  203. # pp diffs.map { |e| e.map { |f| f.to_a } }
  204. 1 class Diff::LCS::ContextDiffCallbacks < Diff::LCS::DiffCallbacks
  205. 1 def discard_a(event)
  206. @hunk << Diff::LCS::ContextChange.simplify(event)
  207. end
  208. 1 def discard_b(event)
  209. @hunk << Diff::LCS::ContextChange.simplify(event)
  210. end
  211. 1 def change(event)
  212. @hunk << Diff::LCS::ContextChange.simplify(event)
  213. end
  214. end
  215. # This will produce a simple array of diff change objects. Each element in
  216. # the #diffs array is a single ContextChange. In the set of #diffs provided
  217. # by SDiffCallbacks, both old and new objects will be presented for both
  218. # changed <strong>and unchanged</strong> objects. +nil+ will be substituted
  219. # for a discarded object.
  220. #
  221. # The diffset produced by this callback, when provided to Diff::LCS#sdiff,
  222. # will compute and display the necessary components to show two sequences
  223. # and their minimized differences side by side, just like the Unix utility
  224. # +sdiff+.
  225. #
  226. # same same
  227. # before | after
  228. # old < -
  229. # - > new
  230. #
  231. # seq1 = %w(a b c e h j l m n p)
  232. # seq2 = %w(b c d e f j k l m r s t)
  233. #
  234. # diffs = Diff::LCS.sdiff(seq1, seq2)
  235. # # This example shows a simplified array format.
  236. # # [ [ "-", [ 0, "a"], [ 0, nil ] ],
  237. # # [ "=", [ 1, "b"], [ 0, "b" ] ],
  238. # # [ "=", [ 2, "c"], [ 1, "c" ] ],
  239. # # [ "+", [ 3, nil], [ 2, "d" ] ],
  240. # # [ "=", [ 3, "e"], [ 3, "e" ] ],
  241. # # [ "!", [ 4, "h"], [ 4, "f" ] ],
  242. # # [ "=", [ 5, "j"], [ 5, "j" ] ],
  243. # # [ "+", [ 6, nil], [ 6, "k" ] ],
  244. # # [ "=", [ 6, "l"], [ 7, "l" ] ],
  245. # # [ "=", [ 7, "m"], [ 8, "m" ] ],
  246. # # [ "!", [ 8, "n"], [ 9, "r" ] ],
  247. # # [ "!", [ 9, "p"], [ 10, "s" ] ],
  248. # # [ "+", [ 10, nil], [ 11, "t" ] ] ]
  249. #
  250. # The result of this operation is similar to that of
  251. # Diff::LCS::ContextDiffCallbacks. They may be compared as:
  252. #
  253. # s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
  254. # c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten(1)
  255. #
  256. # s == c # -> true
  257. #
  258. # === Use
  259. #
  260. # This callback object must be initialised and is used by the Diff::LCS#sdiff
  261. # method.
  262. #
  263. # cbo = Diff::LCS::SDiffCallbacks.new
  264. # Diff::LCS.LCS(seq1, seq2, cbo)
  265. #
  266. # As with the other initialisable callback objects,
  267. # Diff::LCS::SDiffCallbacks can be initialised with a block. As there is no
  268. # "fininishing" to be done, this has no effect on the state of the object.
  269. #
  270. # cbo = Diff::LCS::SDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
  271. #
  272. # === Simplified Array Format
  273. #
  274. # The simplified array format used in the example above can be obtained
  275. # with:
  276. #
  277. # require 'pp'
  278. # pp diffs.map { |e| e.to_a }
  279. 1 class Diff::LCS::SDiffCallbacks
  280. # Returns the difference set collected during the diff process.
  281. 1 attr_reader :diffs
  282. 1 def initialize #:yields self:
  283. @diffs = []
  284. yield self if block_given?
  285. end
  286. 1 def match(event)
  287. @diffs << Diff::LCS::ContextChange.simplify(event)
  288. end
  289. 1 def discard_a(event)
  290. @diffs << Diff::LCS::ContextChange.simplify(event)
  291. end
  292. 1 def discard_b(event)
  293. @diffs << Diff::LCS::ContextChange.simplify(event)
  294. end
  295. 1 def change(event)
  296. @diffs << Diff::LCS::ContextChange.simplify(event)
  297. end
  298. end

vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/change.rb

51.72% lines covered

87 relevant lines. 45 lines covered and 42 lines missed.
    
  1. # frozen_string_literal: true
  2. # Represents a simplistic (non-contextual) change. Represents the removal or
  3. # addition of an element from either the old or the new sequenced
  4. # enumerable.
  5. 1 class Diff::LCS::Change
  6. 1 IntClass = 1.class # Fixnum is deprecated in Ruby 2.4 # rubocop:disable Naming/ConstantName
  7. # The only actions valid for changes are '+' (add), '-' (delete), '='
  8. # (no change), '!' (changed), '<' (tail changes from first sequence), or
  9. # '>' (tail changes from second sequence). The last two ('<>') are only
  10. # found with Diff::LCS::diff and Diff::LCS::sdiff.
  11. 1 VALID_ACTIONS = %w(+ - = ! > <).freeze
  12. 1 def self.valid_action?(action)
  13. 4 VALID_ACTIONS.include? action
  14. end
  15. # Returns the action this Change represents.
  16. 1 attr_reader :action
  17. # Returns the position of the Change.
  18. 1 attr_reader :position
  19. # Returns the sequence element of the Change.
  20. 1 attr_reader :element
  21. 1 def initialize(*args)
  22. 2 @action, @position, @element = *args
  23. 2 fail "Invalid Change Action '#{@action}'" unless Diff::LCS::Change.valid_action?(@action)
  24. 2 fail 'Invalid Position Type' unless @position.kind_of? IntClass
  25. end
  26. 1 def inspect(*_args)
  27. "#<#{self.class}: #{to_a.inspect}>"
  28. end
  29. 1 def to_a
  30. [@action, @position, @element]
  31. end
  32. 1 alias to_ary to_a
  33. 1 def self.from_a(arr)
  34. arr = arr.flatten(1)
  35. case arr.size
  36. when 5
  37. Diff::LCS::ContextChange.new(*(arr[0...5]))
  38. when 3
  39. Diff::LCS::Change.new(*(arr[0...3]))
  40. else
  41. fail 'Invalid change array format provided.'
  42. end
  43. end
  44. 1 include Comparable
  45. 1 def ==(other)
  46. (self.class == other.class) and
  47. (action == other.action) and
  48. (position == other.position) and
  49. (element == other.element)
  50. end
  51. 1 def <=>(other)
  52. r = action <=> other.action
  53. r = position <=> other.position if r.zero?
  54. r = element <=> other.element if r.zero?
  55. r
  56. end
  57. 1 def adding?
  58. 2 @action == '+'
  59. end
  60. 1 def deleting?
  61. 2 @action == '-'
  62. end
  63. 1 def unchanged?
  64. @action == '='
  65. end
  66. 1 def changed?
  67. @action == '!'
  68. end
  69. 1 def finished_a?
  70. @action == '>'
  71. end
  72. 1 def finished_b?
  73. @action == '<'
  74. end
  75. end
  76. # Represents a contextual change. Contains the position and values of the
  77. # elements in the old and the new sequenced enumerables as well as the action
  78. # taken.
  79. 1 class Diff::LCS::ContextChange < Diff::LCS::Change
  80. # We don't need these two values.
  81. 1 undef :position
  82. 1 undef :element
  83. # Returns the old position being changed.
  84. 1 attr_reader :old_position
  85. # Returns the new position being changed.
  86. 1 attr_reader :new_position
  87. # Returns the old element being changed.
  88. 1 attr_reader :old_element
  89. # Returns the new element being changed.
  90. 1 attr_reader :new_element
  91. 1 def initialize(*args)
  92. 2 @action, @old_position, @old_element, @new_position, @new_element = *args
  93. 2 fail "Invalid Change Action '#{@action}'" unless Diff::LCS::Change.valid_action?(@action)
  94. 2 fail 'Invalid (Old) Position Type' unless @old_position.nil? or @old_position.kind_of? IntClass
  95. 2 fail 'Invalid (New) Position Type' unless @new_position.nil? or @new_position.kind_of? IntClass
  96. end
  97. 1 def to_a
  98. [
  99. @action,
  100. [@old_position, @old_element],
  101. [@new_position, @new_element]
  102. ]
  103. end
  104. 1 alias to_ary to_a
  105. 1 def self.from_a(arr)
  106. Diff::LCS::Change.from_a(arr)
  107. end
  108. # Simplifies a context change for use in some diff callbacks. '<' actions
  109. # are converted to '-' and '>' actions are converted to '+'.
  110. 1 def self.simplify(event)
  111. ea = event.to_a
  112. case ea[0]
  113. when '-'
  114. ea[2][1] = nil
  115. when '<'
  116. ea[0] = '-'
  117. ea[2][1] = nil
  118. when '+'
  119. ea[1][1] = nil
  120. when '>'
  121. ea[0] = '+'
  122. ea[1][1] = nil
  123. end
  124. Diff::LCS::ContextChange.from_a(ea)
  125. end
  126. 1 def ==(other)
  127. (self.class == other.class) and
  128. (@action == other.action) and
  129. (@old_position == other.old_position) and
  130. (@new_position == other.new_position) and
  131. (@old_element == other.old_element) and
  132. (@new_element == other.new_element)
  133. end
  134. 1 def <=>(other)
  135. r = @action <=> other.action
  136. r = @old_position <=> other.old_position if r.zero?
  137. r = @new_position <=> other.new_position if r.zero?
  138. r = @old_element <=> other.old_element if r.zero?
  139. r = @new_element <=> other.new_element if r.zero?
  140. r
  141. end
  142. end

vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/hunk.rb

53.07% lines covered

179 relevant lines. 95 lines covered and 84 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require 'diff/lcs/block'
  3. # A Hunk is a group of Blocks which overlap because of the context surrounding
  4. # each block. (So if we're not using context, every hunk will contain one
  5. # block.) Used in the diff program (bin/ldiff).
  6. 1 class Diff::LCS::Hunk
  7. 1 OLD_DIFF_OP_ACTION = { '+' => 'a', '-' => 'd', '!' => 'c' }.freeze #:nodoc:
  8. 1 ED_DIFF_OP_ACTION = { '+' => 'a', '-' => 'd', '!' => 'c' }.freeze #:nodoc:
  9. 1 private_constant :OLD_DIFF_OP_ACTION, :ED_DIFF_OP_ACTION if respond_to?(:private_constant)
  10. # Create a hunk using references to both the old and new data, as well as the
  11. # piece of data.
  12. 1 def initialize(data_old, data_new, piece, flag_context, file_length_difference)
  13. # At first, a hunk will have just one Block in it
  14. 1 @blocks = [Diff::LCS::Block.new(piece)]
  15. 1 if @blocks[0].remove.empty? && @blocks[0].insert.empty?
  16. fail "Cannot build a hunk from #{piece.inspect}; has no add or remove actions"
  17. end
  18. 1 if String.method_defined?(:encoding)
  19. 1 @preferred_data_encoding = data_old.fetch(0, data_new.fetch(0, '')).encoding
  20. end
  21. 1 @data_old = data_old
  22. 1 @data_new = data_new
  23. 1 before = after = file_length_difference
  24. 1 after += @blocks[0].diff_size
  25. 1 @file_length_difference = after # The caller must get this manually
  26. 2 @max_diff_size = @blocks.map { |e| e.diff_size.abs }.max
  27. # Save the start & end of each array. If the array doesn't exist (e.g.,
  28. # we're only adding items in this block), then figure out the line number
  29. # based on the line number of the other file and the current difference in
  30. # file lengths.
  31. 1 if @blocks[0].remove.empty?
  32. a1 = a2 = nil
  33. else
  34. 1 a1 = @blocks[0].remove[0].position
  35. 1 a2 = @blocks[0].remove[-1].position
  36. end
  37. 1 if @blocks[0].insert.empty?
  38. b1 = b2 = nil
  39. else
  40. 1 b1 = @blocks[0].insert[0].position
  41. 1 b2 = @blocks[0].insert[-1].position
  42. end
  43. 1 @start_old = a1 || (b1 - before)
  44. 1 @start_new = b1 || (a1 + before)
  45. 1 @end_old = a2 || (b2 - after)
  46. 1 @end_new = b2 || (a2 + after)
  47. 1 self.flag_context = flag_context
  48. end
  49. 1 attr_reader :blocks
  50. 1 attr_reader :start_old, :start_new
  51. 1 attr_reader :end_old, :end_new
  52. 1 attr_reader :file_length_difference
  53. # Change the "start" and "end" fields to note that context should be added
  54. # to this hunk.
  55. 1 attr_accessor :flag_context # rubocop:disable Layout/EmptyLinesAroundAttributeAccessor
  56. 1 undef :flag_context=
  57. 1 def flag_context=(context) #:nodoc: # rubocop:disable Lint/DuplicateMethods
  58. 1 return if context.nil? or context.zero?
  59. 1 add_start = context > @start_old ? @start_old : context
  60. 1 @start_old -= add_start
  61. 1 @start_new -= add_start
  62. 1 old_size = @data_old.size
  63. add_end =
  64. 1 if (@end_old + context) > old_size
  65. 1 old_size - @end_old
  66. else
  67. context
  68. end
  69. 1 add_end = @max_diff_size if add_end >= old_size
  70. 1 @end_old += add_end
  71. 1 @end_new += add_end
  72. end
  73. # Merges this hunk and the provided hunk together if they overlap. Returns
  74. # a truthy value so that if there is no overlap, you can know the merge
  75. # was skipped.
  76. 1 def merge(hunk)
  77. return unless overlaps?(hunk)
  78. @start_old = hunk.start_old
  79. @start_new = hunk.start_new
  80. blocks.unshift(*hunk.blocks)
  81. end
  82. 1 alias unshift merge
  83. # Determines whether there is an overlap between this hunk and the
  84. # provided hunk. This will be true if the difference between the two hunks
  85. # start or end positions is within one position of each other.
  86. 1 def overlaps?(hunk)
  87. hunk and (((@start_old - hunk.end_old) <= 1) or
  88. ((@start_new - hunk.end_new) <= 1))
  89. end
  90. # Returns a diff string based on a format.
  91. 1 def diff(format, last = false)
  92. 1 case format
  93. when :old
  94. old_diff(last)
  95. when :unified
  96. 1 unified_diff(last)
  97. when :context
  98. context_diff(last)
  99. when :ed
  100. self
  101. when :reverse_ed, :ed_finish
  102. ed_diff(format, last)
  103. else
  104. fail "Unknown diff format #{format}."
  105. end
  106. end
  107. # Note that an old diff can't have any context. Therefore, we know that
  108. # there's only one block in the hunk.
  109. 1 def old_diff(_last = false)
  110. warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
  111. block = @blocks[0]
  112. # Calculate item number range. Old diff range is just like a context
  113. # diff range, except the ranges are on one line with the action between
  114. # them.
  115. s = encode("#{context_range(:old, ',')}#{OLD_DIFF_OP_ACTION[block.op]}#{context_range(:new, ',')}\n")
  116. # If removing anything, just print out all the remove lines in the hunk
  117. # which is just all the remove lines in the block.
  118. unless block.remove.empty?
  119. @data_old[@start_old..@end_old].each { |e| s << encode('< ') + e.chomp + encode("\n") }
  120. end
  121. s << encode("---\n") if block.op == '!'
  122. unless block.insert.empty?
  123. @data_new[@start_new..@end_new].each { |e| s << encode('> ') + e.chomp + encode("\n") }
  124. end
  125. s
  126. end
  127. 1 private :old_diff
  128. 1 def unified_diff(last = false)
  129. # Calculate item number range.
  130. 1 s = encode("@@ -#{unified_range(:old, last)} +#{unified_range(:new, last)} @@\n")
  131. # Outlist starts containing the hunk of the old file. Removing an item
  132. # just means putting a '-' in front of it. Inserting an item requires
  133. # getting it from the new file and splicing it in. We splice in
  134. # +num_added+ items. Remove blocks use +num_added+ because splicing
  135. # changed the length of outlist.
  136. #
  137. # We remove +num_removed+ items. Insert blocks use +num_removed+
  138. # because their item numbers -- corresponding to positions in the NEW
  139. # file -- don't take removed items into account.
  140. 1 lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
  141. 2 outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
  142. 1 last_block = blocks[-1]
  143. 1 if last
  144. old_missing_newline = missing_last_newline?(@data_old)
  145. new_missing_newline = missing_last_newline?(@data_new)
  146. end
  147. 1 @blocks.each do |block|
  148. 1 block.remove.each do |item|
  149. 1 op = item.action.to_s # -
  150. 1 offset = item.position - lo + num_added
  151. 1 outlist[offset][0, 1] = encode(op)
  152. 1 num_removed += 1
  153. end
  154. 1 if last && block == last_block && old_missing_newline && !new_missing_newline
  155. outlist << encode('\\ No newline at end of file')
  156. num_removed += 1
  157. end
  158. 1 block.insert.each do |item|
  159. 1 op = item.action.to_s # +
  160. 1 offset = item.position - @start_new + num_removed
  161. 1 outlist[offset, 0] = encode(op) + @data_new[item.position].chomp
  162. 1 num_added += 1
  163. end
  164. end
  165. 1 outlist << encode('\\ No newline at end of file') if last && new_missing_newline
  166. 1 s << outlist.join(encode("\n"))
  167. 1 s
  168. end
  169. 1 private :unified_diff
  170. 1 def context_diff(last = false)
  171. s = encode("***************\n")
  172. s << encode("*** #{context_range(:old, ',', last)} ****\n")
  173. r = context_range(:new, ',', last)
  174. if last
  175. old_missing_newline = missing_last_newline?(@data_old)
  176. new_missing_newline = missing_last_newline?(@data_new)
  177. end
  178. # Print out file 1 part for each block in context diff format if there
  179. # are any blocks that remove items
  180. lo, hi = @start_old, @end_old
  181. removes = @blocks.reject { |e| e.remove.empty? }
  182. unless removes.empty?
  183. outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
  184. last_block = removes[-1]
  185. removes.each do |block|
  186. block.remove.each do |item|
  187. outlist[item.position - lo][0, 1] = encode(block.op) # - or !
  188. end
  189. if last && block == last_block && old_missing_newline
  190. outlist << encode('\\ No newline at end of file')
  191. end
  192. end
  193. s << outlist.join(encode("\n")) << encode("\n")
  194. end
  195. s << encode("--- #{r} ----\n")
  196. lo, hi = @start_new, @end_new
  197. inserts = @blocks.reject { |e| e.insert.empty? }
  198. unless inserts.empty?
  199. outlist = @data_new[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
  200. last_block = inserts[-1]
  201. inserts.each do |block|
  202. block.insert.each do |item|
  203. outlist[item.position - lo][0, 1] = encode(block.op) # + or !
  204. end
  205. if last && block == last_block && new_missing_newline
  206. outlist << encode('\\ No newline at end of file')
  207. end
  208. end
  209. s << outlist.join(encode("\n"))
  210. end
  211. s
  212. end
  213. 1 private :context_diff
  214. 1 def ed_diff(format, _last = false)
  215. warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
  216. s =
  217. if format == :reverse_ed
  218. encode("#{ED_DIFF_OP_ACTION[@blocks[0].op]}#{context_range(:old, ',')}\n")
  219. else
  220. encode("#{context_range(:old, ' ')}#{ED_DIFF_OP_ACTION[@blocks[0].op]}\n")
  221. end
  222. unless @blocks[0].insert.empty?
  223. @data_new[@start_new..@end_new].each do |e|
  224. s << e.chomp + encode("\n")
  225. end
  226. s << encode(".\n")
  227. end
  228. s
  229. end
  230. 1 private :ed_diff
  231. # Generate a range of item numbers to print. Only print 1 number if the
  232. # range has only one item in it. Otherwise, it's 'start,end'
  233. 1 def context_range(mode, op, last = false)
  234. case mode
  235. when :old
  236. s, e = (@start_old + 1), (@end_old + 1)
  237. when :new
  238. s, e = (@start_new + 1), (@end_new + 1)
  239. end
  240. e -= 1 if last
  241. e = 1 if e.zero?
  242. s < e ? "#{s}#{op}#{e}" : e.to_s
  243. end
  244. 1 private :context_range
  245. # Generate a range of item numbers to print for unified diff. Print number
  246. # where block starts, followed by number of lines in the block
  247. # (don't print number of lines if it's 1)
  248. 1 def unified_range(mode, last)
  249. 2 case mode
  250. when :old
  251. 1 s, e = (@start_old + 1), (@end_old + 1)
  252. when :new
  253. 1 s, e = (@start_new + 1), (@end_new + 1)
  254. end
  255. 2 length = e - s + (last ? 0 : 1)
  256. 2 first = length < 2 ? e : s # "strange, but correct"
  257. 2 length <= 1 ? first.to_s : "#{first},#{length}"
  258. end
  259. 1 private :unified_range
  260. 1 def missing_last_newline?(data)
  261. newline = encode("\n")
  262. if data[-2]
  263. data[-2].end_with?(newline) && !data[-1].end_with?(newline)
  264. elsif data[-1]
  265. !data[-1].end_with?(newline)
  266. else
  267. true
  268. end
  269. end
  270. 1 if String.method_defined?(:encoding)
  271. 1 def encode(literal, target_encoding = @preferred_data_encoding)
  272. 5 literal.encode target_encoding
  273. end
  274. 1 def encode_as(string, *args)
  275. args.map { |arg| arg.encode(string.encoding) }
  276. end
  277. else
  278. def encode(literal, _target_encoding = nil)
  279. literal
  280. end
  281. def encode_as(_string, *args)
  282. args
  283. end
  284. end
  285. 1 private :encode
  286. 1 private :encode_as
  287. end

vender/bundle/ruby/2.6.0/gems/diff-lcs-1.4.4/lib/diff/lcs/internals.rb

30.56% lines covered

144 relevant lines. 44 lines covered and 100 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 class << Diff::LCS
  3. 1 def diff_traversal(method, seq1, seq2, callbacks, &block)
  4. 1 callbacks = callbacks_for(callbacks)
  5. 1 case method
  6. when :diff
  7. 1 traverse_sequences(seq1, seq2, callbacks)
  8. when :sdiff
  9. traverse_balanced(seq1, seq2, callbacks)
  10. end
  11. 1 callbacks.finish if callbacks.respond_to? :finish
  12. 1 if block
  13. callbacks.diffs.map do |hunk|
  14. if hunk.kind_of? Array
  15. hunk.map { |hunk_block| block[hunk_block] }
  16. else
  17. block[hunk]
  18. end
  19. end
  20. else
  21. 1 callbacks.diffs
  22. end
  23. end
  24. 1 private :diff_traversal
  25. end
  26. 1 module Diff::LCS::Internals # :nodoc:
  27. end
  28. 1 class << Diff::LCS::Internals
  29. # Compute the longest common subsequence between the sequenced
  30. # Enumerables +a+ and +b+. The result is an array whose contents is such
  31. # that
  32. #
  33. # result = Diff::LCS::Internals.lcs(a, b)
  34. # result.each_with_index do |e, i|
  35. # assert_equal(a[i], b[e]) unless e.nil?
  36. # end
  37. 1 def lcs(a, b)
  38. 1 a_start = b_start = 0
  39. 1 a_finish = a.size - 1
  40. 1 b_finish = b.size - 1
  41. 1 vector = []
  42. # Prune off any common elements at the beginning...
  43. 2 while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_start] == b[b_start])
  44. vector[a_start] = b_start
  45. a_start += 1
  46. b_start += 1
  47. end
  48. 1 b_start = a_start
  49. # Now the end...
  50. 2 while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_finish] == b[b_finish])
  51. vector[a_finish] = b_finish
  52. a_finish -= 1
  53. b_finish -= 1
  54. end
  55. # Now, compute the equivalence classes of positions of elements.
  56. 1 b_matches = position_hash(b, b_start..b_finish)
  57. 1 thresh = []
  58. 1 links = []
  59. 1 string = a.kind_of?(String)
  60. 1 (a_start..a_finish).each do |i|
  61. 1 ai = string ? a[i, 1] : a[i]
  62. 1 bm = b_matches[ai]
  63. 1 k = nil
  64. 1 bm.reverse_each do |j|
  65. if k and (thresh[k] > j) and (thresh[k - 1] < j)
  66. thresh[k] = j
  67. else
  68. k = replace_next_larger(thresh, j, k)
  69. end
  70. links[k] = [k.positive? ? links[k - 1] : nil, i, j] unless k.nil?
  71. end
  72. end
  73. 1 unless thresh.empty?
  74. link = links[thresh.size - 1]
  75. until link.nil?
  76. vector[link[1]] = link[2]
  77. link = link[0]
  78. end
  79. end
  80. 1 vector
  81. end
  82. # This method will analyze the provided patchset to provide a single-pass
  83. # normalization (conversion of the array form of Diff::LCS::Change objects to
  84. # the object form of same) and detection of whether the patchset represents
  85. # changes to be made.
  86. 1 def analyze_patchset(patchset, depth = 0)
  87. fail 'Patchset too complex' if depth > 1
  88. has_changes = false
  89. new_patchset = []
  90. # Format:
  91. # [ # patchset
  92. # # hunk (change)
  93. # [ # hunk
  94. # # change
  95. # ]
  96. # ]
  97. patchset.each do |hunk|
  98. case hunk
  99. when Diff::LCS::Change
  100. has_changes ||= !hunk.unchanged?
  101. new_patchset << hunk
  102. when Array
  103. # Detect if the 'hunk' is actually an array-format change object.
  104. if Diff::LCS::Change.valid_action? hunk[0]
  105. hunk = Diff::LCS::Change.from_a(hunk)
  106. has_changes ||= !hunk.unchanged?
  107. new_patchset << hunk
  108. else
  109. with_changes, hunk = analyze_patchset(hunk, depth + 1)
  110. has_changes ||= with_changes
  111. new_patchset.concat(hunk)
  112. end
  113. else
  114. fail ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
  115. end
  116. end
  117. [has_changes, new_patchset]
  118. end
  119. # Examine the patchset and the source to see in which direction the
  120. # patch should be applied.
  121. #
  122. # WARNING: By default, this examines the whole patch, so this could take
  123. # some time. This also works better with Diff::LCS::ContextChange or
  124. # Diff::LCS::Change as its source, as an array will cause the creation
  125. # of one of the above.
  126. 1 def intuit_diff_direction(src, patchset, limit = nil)
  127. string = src.kind_of?(String)
  128. count = left_match = left_miss = right_match = right_miss = 0
  129. patchset.each do |change|
  130. count += 1
  131. case change
  132. when Diff::LCS::ContextChange
  133. le = string ? src[change.old_position, 1] : src[change.old_position]
  134. re = string ? src[change.new_position, 1] : src[change.new_position]
  135. case change.action
  136. when '-' # Remove details from the old string
  137. if le == change.old_element
  138. left_match += 1
  139. else
  140. left_miss += 1
  141. end
  142. when '+'
  143. if re == change.new_element
  144. right_match += 1
  145. else
  146. right_miss += 1
  147. end
  148. when '='
  149. left_miss += 1 if le != change.old_element
  150. right_miss += 1 if re != change.new_element
  151. when '!'
  152. if le == change.old_element
  153. left_match += 1
  154. elsif re == change.new_element
  155. right_match += 1
  156. else
  157. left_miss += 1
  158. right_miss += 1
  159. end
  160. end
  161. when Diff::LCS::Change
  162. # With a simplistic change, we can't tell the difference between
  163. # the left and right on '!' actions, so we ignore those. On '='
  164. # actions, if there's a miss, we miss both left and right.
  165. element = string ? src[change.position, 1] : src[change.position]
  166. case change.action
  167. when '-'
  168. if element == change.element
  169. left_match += 1
  170. else
  171. left_miss += 1
  172. end
  173. when '+'
  174. if element == change.element
  175. right_match += 1
  176. else
  177. right_miss += 1
  178. end
  179. when '='
  180. if element != change.element
  181. left_miss += 1
  182. right_miss += 1
  183. end
  184. end
  185. end
  186. break if !limit.nil? && (count > limit)
  187. end
  188. no_left = left_match.zero? && left_miss.positive?
  189. no_right = right_match.zero? && right_miss.positive?
  190. case [no_left, no_right]
  191. when [false, true]
  192. :patch
  193. when [true, false]
  194. :unpatch
  195. else
  196. case left_match <=> right_match
  197. when 1
  198. if left_miss.zero?
  199. :patch
  200. else
  201. :unpatch
  202. end
  203. when -1
  204. if right_miss.zero?
  205. :unpatch
  206. else
  207. :patch
  208. end
  209. else
  210. fail "The provided patchset does not appear to apply to the provided \
  211. enumerable as either source or destination value."
  212. end
  213. end
  214. end
  215. # Find the place at which +value+ would normally be inserted into the
  216. # Enumerable. If that place is already occupied by +value+, do nothing
  217. # and return +nil+. If the place does not exist (i.e., it is off the end
  218. # of the Enumerable), add it to the end. Otherwise, replace the element
  219. # at that point with +value+. It is assumed that the Enumerable's values
  220. # are numeric.
  221. #
  222. # This operation preserves the sort order.
  223. 1 def replace_next_larger(enum, value, last_index = nil)
  224. # Off the end?
  225. if enum.empty? or (value > enum[-1])
  226. enum << value
  227. return enum.size - 1
  228. end
  229. # Binary search for the insertion point
  230. last_index ||= enum.size
  231. first_index = 0
  232. while first_index <= last_index
  233. i = (first_index + last_index) >> 1
  234. found = enum[i]
  235. return nil if value == found
  236. if value > found
  237. first_index = i + 1
  238. else
  239. last_index = i - 1
  240. end
  241. end
  242. # The insertion point is in first_index; overwrite the next larger
  243. # value.
  244. enum[first_index] = value
  245. first_index
  246. end
  247. 1 private :replace_next_larger
  248. # If +vector+ maps the matching elements of another collection onto this
  249. # Enumerable, compute the inverse of +vector+ that maps this Enumerable
  250. # onto the collection. (Currently unused.)
  251. 1 def inverse_vector(a, vector)
  252. inverse = a.dup
  253. (0...vector.size).each do |i|
  254. inverse[vector[i]] = i unless vector[i].nil?
  255. end
  256. inverse
  257. end
  258. 1 private :inverse_vector
  259. # Returns a hash mapping each element of an Enumerable to the set of
  260. # positions it occupies in the Enumerable, optionally restricted to the
  261. # elements specified in the range of indexes specified by +interval+.
  262. 1 def position_hash(enum, interval)
  263. 1 string = enum.kind_of?(String)
  264. 3 hash = Hash.new { |h, k| h[k] = [] }
  265. 1 interval.each do |i|
  266. 1 k = string ? enum[i, 1] : enum[i]
  267. 1 hash[k] << i
  268. end
  269. 1 hash
  270. end
  271. 1 private :position_hash
  272. end

vender/bundle/ruby/2.6.0/gems/erubi-1.10.0/lib/erubi.rb

17.65% lines covered

102 relevant lines. 18 lines covered and 84 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Erubi
  3. 1 VERSION = '1.10.0'
  4. 1 RANGE_ALL = 0..-1
  5. skipped # :nocov:
  6. skipped if RUBY_VERSION >= '1.9'
  7. skipped RANGE_FIRST = 0
  8. skipped RANGE_LAST = -1
  9. skipped else
  10. skipped RANGE_FIRST = 0..0
  11. skipped RANGE_LAST = -1..-1
  12. skipped end
  13. skipped
  14. skipped TEXT_END = RUBY_VERSION >= '2.1' ? "'.freeze;" : "';"
  15. skipped MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
  16. skipped # :nocov:
  17. begin
  18. 1 require 'cgi/escape'
  19. skipped # :nocov:
  20. skipped unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
  21. skipped CGI = Object.new
  22. skipped CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
  23. skipped end
  24. skipped # :nocov:
  25. # Escape characters with their HTML/XML equivalents.
  26. 1 def self.h(value)
  27. CGI.escapeHTML(value.to_s)
  28. end
  29. rescue LoadError
  30. skipped # :nocov:
  31. skipped ESCAPE_TABLE = {'&' => '&amp;'.freeze, '<' => '&lt;'.freeze, '>' => '&gt;'.freeze, '"' => '&quot;'.freeze, "'" => '&#39;'.freeze}.freeze
  32. skipped if RUBY_VERSION >= '1.9'
  33. skipped def self.h(value)
  34. skipped value.to_s.gsub(/[&<>"']/, ESCAPE_TABLE)
  35. skipped end
  36. skipped else
  37. skipped def self.h(value)
  38. skipped value.to_s.gsub(/[&<>"']/){|s| ESCAPE_TABLE[s]}
  39. skipped end
  40. skipped end
  41. skipped # :nocov:
  42. end
  43. 1 class Engine
  44. # The frozen ruby source code generated from the template, which can be evaled.
  45. 1 attr_reader :src
  46. # The filename of the template, if one was given.
  47. 1 attr_reader :filename
  48. # The variable name used for the buffer variable.
  49. 1 attr_reader :bufvar
  50. # Initialize a new Erubi::Engine. Options:
  51. # +:bufval+ :: The value to use for the buffer variable, as a string (default <tt>'::String.new'</tt>).
  52. # +:bufvar+ :: The variable name to use for the buffer variable, as a string.
  53. # +:ensure+ :: Wrap the template in a begin/ensure block restoring the previous value of bufvar.
  54. # +:escapefunc+ :: The function to use for escaping, as a string (default: <tt>'::Erubi.h'</tt>).
  55. # +:escape+ :: Whether to make <tt><%=</tt> escape by default, and <tt><%==</tt> not escape by default.
  56. # +:escape_html+ :: Same as +:escape+, with lower priority.
  57. # +:filename+ :: The filename for the template.
  58. # +:freeze+ :: Whether to enable frozen string literals in the resulting source code.
  59. # +:literal_prefix+ :: The prefix to output when using escaped tag delimiters (default <tt>'<%'</tt>).
  60. # +:literal_postfix+ :: The postfix to output when using escaped tag delimiters (default <tt>'%>'</tt>).
  61. # +:outvar+ :: Same as +:bufvar+, with lower priority.
  62. # +:postamble+ :: The postamble for the template, by default returns the resulting source code.
  63. # +:preamble+ :: The preamble for the template, by default initializes the buffer variable.
  64. # +:regexp+ :: The regexp to use for scanning.
  65. # +:src+ :: The initial value to use for the source code, an empty string by default.
  66. # +:trim+ :: Whether to trim leading and trailing whitespace, true by default.
  67. 1 def initialize(input, properties={})
  68. @escape = escape = properties.fetch(:escape){properties.fetch(:escape_html, false)}
  69. trim = properties[:trim] != false
  70. @filename = properties[:filename]
  71. @bufvar = bufvar = properties[:bufvar] || properties[:outvar] || "_buf"
  72. bufval = properties[:bufval] || '::String.new'
  73. regexp = properties[:regexp] || /<%(={1,2}|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m
  74. literal_prefix = properties[:literal_prefix] || '<%'
  75. literal_postfix = properties[:literal_postfix] || '%>'
  76. preamble = properties[:preamble] || "#{bufvar} = #{bufval};"
  77. postamble = properties[:postamble] || "#{bufvar}.to_s\n"
  78. @src = src = properties[:src] || String.new
  79. src << "# frozen_string_literal: true\n" if properties[:freeze]
  80. src << "begin; __original_outvar = #{bufvar} if defined?(#{bufvar}); " if properties[:ensure]
  81. unless @escapefunc = properties[:escapefunc]
  82. if escape
  83. @escapefunc = '__erubi.h'
  84. src << "__erubi = ::Erubi;"
  85. else
  86. @escapefunc = '::Erubi.h'
  87. end
  88. end
  89. src << preamble
  90. pos = 0
  91. is_bol = true
  92. input.scan(regexp) do |indicator, code, tailch, rspace|
  93. match = Regexp.last_match
  94. len = match.begin(0) - pos
  95. text = input[pos, len]
  96. pos = match.end(0)
  97. ch = indicator ? indicator[RANGE_FIRST] : nil
  98. lspace = nil
  99. unless ch == '='
  100. if text.empty?
  101. lspace = "" if is_bol
  102. elsif text[RANGE_LAST] == "\n"
  103. lspace = ""
  104. else
  105. rindex = text.rindex("\n")
  106. if rindex
  107. range = rindex+1..-1
  108. s = text[range]
  109. if /\A[ \t]*\z/.send(MATCH_METHOD, s)
  110. lspace = s
  111. text[range] = ''
  112. end
  113. else
  114. if is_bol && /\A[ \t]*\z/.send(MATCH_METHOD, text)
  115. lspace = text
  116. text = ''
  117. end
  118. end
  119. end
  120. end
  121. is_bol = rspace
  122. add_text(text)
  123. case ch
  124. when '='
  125. rspace = nil if tailch && !tailch.empty?
  126. add_expression(indicator, code)
  127. add_text(rspace) if rspace
  128. when nil, '-'
  129. if trim && lspace && rspace
  130. add_code("#{lspace}#{code}#{rspace}")
  131. else
  132. add_text(lspace) if lspace
  133. add_code(code)
  134. add_text(rspace) if rspace
  135. end
  136. when '#'
  137. n = code.count("\n") + (rspace ? 1 : 0)
  138. if trim && lspace && rspace
  139. add_code("\n" * n)
  140. else
  141. add_text(lspace) if lspace
  142. add_code("\n" * n)
  143. add_text(rspace) if rspace
  144. end
  145. when '%'
  146. add_text("#{lspace}#{literal_prefix}#{code}#{tailch}#{literal_postfix}#{rspace}")
  147. else
  148. handle(indicator, code, tailch, rspace, lspace)
  149. end
  150. end
  151. rest = pos == 0 ? input : input[pos..-1]
  152. add_text(rest)
  153. src << "\n" unless src[RANGE_LAST] == "\n"
  154. add_postamble(postamble)
  155. src << "; ensure\n " << bufvar << " = __original_outvar\nend\n" if properties[:ensure]
  156. src.freeze
  157. freeze
  158. end
  159. 1 private
  160. # Add raw text to the template. Modifies argument if argument is mutable as a memory optimization.
  161. # Must be called with a string, cannot be called with nil (Rails's subclass depends on it).
  162. 1 def add_text(text)
  163. return if text.empty?
  164. if text.frozen?
  165. text = text.gsub(/['\\]/, '\\\\\&')
  166. else
  167. text.gsub!(/['\\]/, '\\\\\&')
  168. end
  169. @src << " " << @bufvar << " << '" << text << TEXT_END
  170. end
  171. # Add ruby code to the template
  172. 1 def add_code(code)
  173. @src << code
  174. @src << ';' unless code[RANGE_LAST] == "\n"
  175. end
  176. # Add the given ruby expression result to the template,
  177. # escaping it based on the indicator given and escape flag.
  178. 1 def add_expression(indicator, code)
  179. if ((indicator == '=') ^ @escape)
  180. add_expression_result(code)
  181. else
  182. add_expression_result_escaped(code)
  183. end
  184. end
  185. # Add the result of Ruby expression to the template
  186. 1 def add_expression_result(code)
  187. @src << ' ' << @bufvar << ' << (' << code << ').to_s;'
  188. end
  189. # Add the escaped result of Ruby expression to the template
  190. 1 def add_expression_result_escaped(code)
  191. @src << ' ' << @bufvar << ' << ' << @escapefunc << '((' << code << '));'
  192. end
  193. # Add the given postamble to the src. Can be overridden in subclasses
  194. # to make additional changes to src that depend on the current state.
  195. 1 def add_postamble(postamble)
  196. src << postamble
  197. end
  198. # Raise an exception, as the base engine class does not support handling other indicators.
  199. 1 def handle(indicator, code, tailch, rspace, lspace)
  200. raise ArgumentError, "Invalid indicator: #{indicator}"
  201. end
  202. end
  203. end

vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/check_delivery_params.rb

31.03% lines covered

29 relevant lines. 9 lines covered and 20 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Mail
  3. 1 module CheckDeliveryParams #:nodoc:
  4. 1 class << self
  5. 1 def check(mail)
  6. [ check_from(mail.smtp_envelope_from),
  7. check_to(mail.smtp_envelope_to),
  8. check_message(mail) ]
  9. end
  10. 1 def check_from(addr)
  11. if Utilities.blank?(addr)
  12. raise ArgumentError, "SMTP From address may not be blank: #{addr.inspect}"
  13. end
  14. check_addr 'From', addr
  15. end
  16. 1 def check_to(addrs)
  17. if Utilities.blank?(addrs)
  18. raise ArgumentError, "SMTP To address may not be blank: #{addrs.inspect}"
  19. end
  20. Array(addrs).map do |addr|
  21. check_addr 'To', addr
  22. end
  23. end
  24. 1 def check_addr(addr_name, addr)
  25. validate_smtp_addr addr do |error_message|
  26. raise ArgumentError, "SMTP #{addr_name} address #{error_message}: #{addr.inspect}"
  27. end
  28. end
  29. 1 def validate_smtp_addr(addr)
  30. if addr
  31. if addr.bytesize > 2048
  32. yield 'may not exceed 2kB'
  33. end
  34. if /[\r\n]/ =~ addr
  35. yield 'may not contain CR or LF line breaks'
  36. end
  37. end
  38. addr
  39. end
  40. 1 def check_message(message)
  41. message = message.encoded if message.respond_to?(:encoded)
  42. if Utilities.blank?(message)
  43. raise ArgumentError, 'An encoded message is required to send an email'
  44. end
  45. message
  46. end
  47. end
  48. end
  49. end

vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/file_delivery.rb

50.0% lines covered

16 relevant lines. 8 lines covered and 8 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require 'mail/check_delivery_params'
  3. 1 module Mail
  4. # FileDelivery class delivers emails into multiple files based on the destination
  5. # address. Each file is appended to if it already exists.
  6. #
  7. # So if you have an email going to fred@test, bob@test, joe@anothertest, and you
  8. # set your location path to /path/to/mails then FileDelivery will create the directory
  9. # if it does not exist, and put one copy of the email in three files, called
  10. # by their message id
  11. #
  12. # Make sure the path you specify with :location is writable by the Ruby process
  13. # running Mail.
  14. 1 class FileDelivery
  15. 1 if RUBY_VERSION >= '1.9.1'
  16. 1 require 'fileutils'
  17. else
  18. require 'ftools'
  19. end
  20. 1 attr_accessor :settings
  21. 1 def initialize(values)
  22. self.settings = { :location => './mails' }.merge!(values)
  23. end
  24. 1 def deliver!(mail)
  25. Mail::CheckDeliveryParams.check(mail)
  26. if ::File.respond_to?(:makedirs)
  27. ::File.makedirs settings[:location]
  28. else
  29. ::FileUtils.mkdir_p settings[:location]
  30. end
  31. mail.destinations.uniq.each do |to|
  32. ::File.open(::File.join(settings[:location], File.basename(to.to_s)), 'a') { |f| "#{f.write(mail.encoded)}\r\n\r\n" }
  33. end
  34. end
  35. end
  36. end

vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/sendmail.rb

44.0% lines covered

25 relevant lines. 11 lines covered and 14 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require 'mail/check_delivery_params'
  3. 1 module Mail
  4. # A delivery method implementation which sends via sendmail.
  5. #
  6. # To use this, first find out where the sendmail binary is on your computer,
  7. # if you are on a mac or unix box, it is usually in /usr/sbin/sendmail, this will
  8. # be your sendmail location.
  9. #
  10. # Mail.defaults do
  11. # delivery_method :sendmail
  12. # end
  13. #
  14. # Or if your sendmail binary is not at '/usr/sbin/sendmail'
  15. #
  16. # Mail.defaults do
  17. # delivery_method :sendmail, :location => '/absolute/path/to/your/sendmail'
  18. # end
  19. #
  20. # Then just deliver the email as normal:
  21. #
  22. # Mail.deliver do
  23. # to 'mikel@test.lindsaar.net'
  24. # from 'ada@test.lindsaar.net'
  25. # subject 'testing sendmail'
  26. # body 'testing sendmail'
  27. # end
  28. #
  29. # Or by calling deliver on a Mail message
  30. #
  31. # mail = Mail.new do
  32. # to 'mikel@test.lindsaar.net'
  33. # from 'ada@test.lindsaar.net'
  34. # subject 'testing sendmail'
  35. # body 'testing sendmail'
  36. # end
  37. #
  38. # mail.deliver!
  39. 1 class Sendmail
  40. DEFAULTS = {
  41. 1 :location => '/usr/sbin/sendmail',
  42. :arguments => '-i'
  43. }
  44. 1 attr_accessor :settings
  45. 1 def initialize(values)
  46. self.settings = self.class::DEFAULTS.merge(values)
  47. end
  48. 1 def deliver!(mail)
  49. smtp_from, smtp_to, message = Mail::CheckDeliveryParams.check(mail)
  50. from = "-f #{self.class.shellquote(smtp_from)}" if smtp_from
  51. to = smtp_to.map { |_to| self.class.shellquote(_to) }.join(' ')
  52. arguments = "#{settings[:arguments]} #{from} --"
  53. self.class.call(settings[:location], arguments, to, message)
  54. end
  55. 1 def self.call(path, arguments, destinations, encoded_message)
  56. popen "#{path} #{arguments} #{destinations}" do |io|
  57. io.puts ::Mail::Utilities.binary_unsafe_to_lf(encoded_message)
  58. io.flush
  59. end
  60. end
  61. 1 if RUBY_VERSION < '1.9.0'
  62. def self.popen(command, &block)
  63. IO.popen "#{command} 2>&1", 'w+', &block
  64. end
  65. else
  66. 1 def self.popen(command, &block)
  67. IO.popen command, 'w+', :err => :out, &block
  68. end
  69. end
  70. # The following is an adaptation of ruby 1.9.2's shellwords.rb file,
  71. # with the following modifications:
  72. #
  73. # - Wraps in double quotes
  74. # - Allows '+' to accept email addresses with them
  75. # - Allows '~' as it is not unescaped in double quotes
  76. 1 def self.shellquote(address)
  77. # Process as a single byte sequence because not all shell
  78. # implementations are multibyte aware.
  79. #
  80. # A LF cannot be escaped with a backslash because a backslash + LF
  81. # combo is regarded as line continuation and simply ignored. Strip it.
  82. escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@~])/n, "\\\\\\1").gsub("\n", '')
  83. %("#{escaped}")
  84. end
  85. end
  86. end

vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/smtp.rb

30.56% lines covered

36 relevant lines. 11 lines covered and 25 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require 'mail/check_delivery_params'
  3. 1 module Mail
  4. # == Sending Email with SMTP
  5. #
  6. # Mail allows you to send emails using SMTP. This is done by wrapping Net::SMTP in
  7. # an easy to use manner.
  8. #
  9. # === Sending via SMTP server on Localhost
  10. #
  11. # Sending locally (to a postfix or sendmail server running on localhost) requires
  12. # no special setup. Just to Mail.deliver &block or message.deliver! and it will
  13. # be sent in this method.
  14. #
  15. # === Sending via MobileMe
  16. #
  17. # Mail.defaults do
  18. # delivery_method :smtp, { :address => "smtp.me.com",
  19. # :port => 587,
  20. # :domain => 'your.host.name',
  21. # :user_name => '<username>',
  22. # :password => '<password>',
  23. # :authentication => 'plain',
  24. # :enable_starttls_auto => true }
  25. # end
  26. #
  27. # === Sending via GMail
  28. #
  29. # Mail.defaults do
  30. # delivery_method :smtp, { :address => "smtp.gmail.com",
  31. # :port => 587,
  32. # :domain => 'your.host.name',
  33. # :user_name => '<username>',
  34. # :password => '<password>',
  35. # :authentication => 'plain',
  36. # :enable_starttls_auto => true }
  37. # end
  38. #
  39. # === Certificate verification
  40. #
  41. # When using TLS, some mail servers provide certificates that are self-signed
  42. # or whose names do not exactly match the hostname given in the address.
  43. # OpenSSL will reject these by default. The best remedy is to use the correct
  44. # hostname or update the certificate authorities trusted by your ruby. If
  45. # that isn't possible, you can control this behavior with
  46. # an :openssl_verify_mode setting. Its value may be either an OpenSSL
  47. # verify mode constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER),
  48. # or a string containing the name of an OpenSSL verify mode (none, peer).
  49. #
  50. # === Others
  51. #
  52. # Feel free to send me other examples that were tricky
  53. #
  54. # === Delivering the email
  55. #
  56. # Once you have the settings right, sending the email is done by:
  57. #
  58. # Mail.deliver do
  59. # to 'mikel@test.lindsaar.net'
  60. # from 'ada@test.lindsaar.net'
  61. # subject 'testing sendmail'
  62. # body 'testing sendmail'
  63. # end
  64. #
  65. # Or by calling deliver on a Mail message
  66. #
  67. # mail = Mail.new do
  68. # to 'mikel@test.lindsaar.net'
  69. # from 'ada@test.lindsaar.net'
  70. # subject 'testing sendmail'
  71. # body 'testing sendmail'
  72. # end
  73. #
  74. # mail.deliver!
  75. 1 class SMTP
  76. 1 attr_accessor :settings
  77. DEFAULTS = {
  78. 1 :address => 'localhost',
  79. :port => 25,
  80. :domain => 'localhost.localdomain',
  81. :user_name => nil,
  82. :password => nil,
  83. :authentication => nil,
  84. :enable_starttls => nil,
  85. :enable_starttls_auto => true,
  86. :openssl_verify_mode => nil,
  87. :ssl => nil,
  88. :tls => nil,
  89. :open_timeout => nil,
  90. :read_timeout => nil
  91. }
  92. 1 def initialize(values)
  93. self.settings = DEFAULTS.merge(values)
  94. end
  95. 1 def deliver!(mail)
  96. response = start_smtp_session do |smtp|
  97. Mail::SMTPConnection.new(:connection => smtp, :return_response => true).deliver!(mail)
  98. end
  99. settings[:return_response] ? response : self
  100. end
  101. 1 private
  102. 1 def start_smtp_session(&block)
  103. build_smtp_session.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication], &block)
  104. end
  105. 1 def build_smtp_session
  106. Net::SMTP.new(settings[:address], settings[:port]).tap do |smtp|
  107. if settings[:tls] || settings[:ssl]
  108. if smtp.respond_to?(:enable_tls)
  109. smtp.enable_tls(ssl_context)
  110. end
  111. elsif settings[:enable_starttls]
  112. if smtp.respond_to?(:enable_starttls)
  113. smtp.enable_starttls(ssl_context)
  114. end
  115. elsif settings[:enable_starttls_auto]
  116. if smtp.respond_to?(:enable_starttls_auto)
  117. smtp.enable_starttls_auto(ssl_context)
  118. end
  119. end
  120. smtp.open_timeout = settings[:open_timeout] if settings[:open_timeout]
  121. smtp.read_timeout = settings[:read_timeout] if settings[:read_timeout]
  122. end
  123. end
  124. # Allow SSL context to be configured via settings, for Ruby >= 1.9
  125. # Just returns openssl verify mode for Ruby 1.8.x
  126. 1 def ssl_context
  127. openssl_verify_mode = settings[:openssl_verify_mode]
  128. if openssl_verify_mode.kind_of?(String)
  129. openssl_verify_mode = OpenSSL::SSL.const_get("VERIFY_#{openssl_verify_mode.upcase}")
  130. end
  131. context = Net::SMTP.default_ssl_context
  132. context.verify_mode = openssl_verify_mode if openssl_verify_mode
  133. context.ca_path = settings[:ca_path] if settings[:ca_path]
  134. context.ca_file = settings[:ca_file] if settings[:ca_file]
  135. context
  136. end
  137. end
  138. end

vender/bundle/ruby/2.6.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/test_mailer.rb

69.23% lines covered

13 relevant lines. 9 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require 'mail/check_delivery_params'
  3. 1 module Mail
  4. # The TestMailer is a bare bones mailer that does nothing. It is useful
  5. # when you are testing.
  6. #
  7. # It also provides a template of the minimum methods you require to implement
  8. # if you want to make a custom mailer for Mail
  9. 1 class TestMailer
  10. # Provides a store of all the emails sent with the TestMailer so you can check them.
  11. 1 def self.deliveries
  12. 22 @@deliveries ||= []
  13. end
  14. # Allows you to over write the default deliveries store from an array to some
  15. # other object. If you just want to clear the store,
  16. # call TestMailer.deliveries.clear.
  17. #
  18. # If you place another object here, please make sure it responds to:
  19. #
  20. # * << (message)
  21. # * clear
  22. # * length
  23. # * size
  24. # * and other common Array methods
  25. 1 def self.deliveries=(val)
  26. @@deliveries = val
  27. end
  28. 1 attr_accessor :settings
  29. 1 def initialize(values)
  30. @settings = values.dup
  31. end
  32. 1 def deliver!(mail)
  33. Mail::CheckDeliveryParams.check(mail)
  34. Mail::TestMailer.deliveries << mail
  35. end
  36. end
  37. end

vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest.rb

37.71% lines covered

411 relevant lines. 155 lines covered and 256 lines missed.
    
  1. 1 require "optparse"
  2. 1 require "thread"
  3. 1 require "mutex_m"
  4. 1 require "minitest/parallel"
  5. 1 require "stringio"
  6. ##
  7. # :include: README.rdoc
  8. 1 module Minitest
  9. 1 VERSION = "5.14.4" # :nodoc:
  10. 1 ENCS = "".respond_to? :encoding # :nodoc:
  11. 1 @@installed_at_exit ||= false
  12. 1 @@after_run = []
  13. 1 @extensions = []
  14. 2 mc = (class << self; self; end)
  15. ##
  16. # Parallel test executor
  17. 1 mc.send :attr_accessor, :parallel_executor
  18. 1 warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"]
  19. 1 n_threads = (ENV["MT_CPU"] || ENV["N"] || 2).to_i
  20. 1 self.parallel_executor = Parallel::Executor.new n_threads
  21. ##
  22. # Filter object for backtraces.
  23. 1 mc.send :attr_accessor, :backtrace_filter
  24. ##
  25. # Reporter object to be used for all runs.
  26. #
  27. # NOTE: This accessor is only available during setup, not during runs.
  28. 1 mc.send :attr_accessor, :reporter
  29. ##
  30. # Names of known extension plugins.
  31. 1 mc.send :attr_accessor, :extensions
  32. ##
  33. # The signal to use for dumping information to STDERR. Defaults to "INFO".
  34. 1 mc.send :attr_accessor, :info_signal
  35. 1 self.info_signal = "INFO"
  36. ##
  37. # Registers Minitest to run at process exit
  38. 1 def self.autorun
  39. at_exit {
  40. next if $! and not ($!.kind_of? SystemExit and $!.success?)
  41. exit_code = nil
  42. pid = Process.pid
  43. at_exit {
  44. next if Process.pid != pid
  45. @@after_run.reverse_each(&:call)
  46. exit exit_code || false
  47. }
  48. exit_code = Minitest.run ARGV
  49. } unless @@installed_at_exit
  50. @@installed_at_exit = true
  51. end
  52. ##
  53. # A simple hook allowing you to run a block of code after everything
  54. # is done running. Eg:
  55. #
  56. # Minitest.after_run { p $debugging_info }
  57. 1 def self.after_run &block
  58. @@after_run << block
  59. end
  60. 1 def self.init_plugins options # :nodoc:
  61. self.extensions.each do |name|
  62. msg = "plugin_#{name}_init"
  63. send msg, options if self.respond_to? msg
  64. end
  65. end
  66. 1 def self.load_plugins # :nodoc:
  67. return unless self.extensions.empty?
  68. seen = {}
  69. require "rubygems" unless defined? Gem
  70. Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
  71. name = File.basename plugin_path, "_plugin.rb"
  72. next if seen[name]
  73. seen[name] = true
  74. require plugin_path
  75. self.extensions << name
  76. end
  77. end
  78. ##
  79. # This is the top-level run method. Everything starts from here. It
  80. # tells each Runnable sub-class to run, and each of those are
  81. # responsible for doing whatever they do.
  82. #
  83. # The overall structure of a run looks like this:
  84. #
  85. # Minitest.autorun
  86. # Minitest.run(args)
  87. # Minitest.__run(reporter, options)
  88. # Runnable.runnables.each
  89. # runnable.run(reporter, options)
  90. # self.runnable_methods.each
  91. # self.run_one_method(self, runnable_method, reporter)
  92. # Minitest.run_one_method(klass, runnable_method)
  93. # klass.new(runnable_method).run
  94. 1 def self.run args = []
  95. self.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
  96. options = process_args args
  97. reporter = CompositeReporter.new
  98. reporter << SummaryReporter.new(options[:io], options)
  99. reporter << ProgressReporter.new(options[:io], options)
  100. self.reporter = reporter # this makes it available to plugins
  101. self.init_plugins options
  102. self.reporter = nil # runnables shouldn't depend on the reporter, ever
  103. self.parallel_executor.start if parallel_executor.respond_to?(:start)
  104. reporter.start
  105. begin
  106. __run reporter, options
  107. rescue Interrupt
  108. warn "Interrupted. Exiting..."
  109. end
  110. self.parallel_executor.shutdown
  111. reporter.report
  112. reporter.passed?
  113. end
  114. ##
  115. # Internal run method. Responsible for telling all Runnable
  116. # sub-classes to run.
  117. 1 def self.__run reporter, options
  118. suites = Runnable.runnables.reject { |s| s.runnable_methods.empty? }.shuffle
  119. parallel, serial = suites.partition { |s| s.test_order == :parallel }
  120. # If we run the parallel tests before the serial tests, the parallel tests
  121. # could run in parallel with the serial tests. This would be bad because
  122. # the serial tests won't lock around Reporter#record. Run the serial tests
  123. # first, so that after they complete, the parallel tests will lock when
  124. # recording results.
  125. serial.map { |suite| suite.run reporter, options } +
  126. parallel.map { |suite| suite.run reporter, options }
  127. end
  128. 1 def self.process_args args = [] # :nodoc:
  129. options = {
  130. :io => $stdout,
  131. }
  132. orig_args = args.dup
  133. OptionParser.new do |opts|
  134. opts.banner = "minitest options:"
  135. opts.version = Minitest::VERSION
  136. opts.on "-h", "--help", "Display this help." do
  137. puts opts
  138. exit
  139. end
  140. opts.on "--no-plugins", "Bypass minitest plugin auto-loading (or set $MT_NO_PLUGINS)."
  141. desc = "Sets random seed. Also via env. Eg: SEED=n rake"
  142. opts.on "-s", "--seed SEED", Integer, desc do |m|
  143. options[:seed] = m.to_i
  144. end
  145. opts.on "-v", "--verbose", "Verbose. Show progress processing files." do
  146. options[:verbose] = true
  147. end
  148. opts.on "-n", "--name PATTERN", "Filter run on /regexp/ or string." do |a|
  149. options[:filter] = a
  150. end
  151. opts.on "-e", "--exclude PATTERN", "Exclude /regexp/ or string from run." do |a|
  152. options[:exclude] = a
  153. end
  154. unless extensions.empty?
  155. opts.separator ""
  156. opts.separator "Known extensions: #{extensions.join(", ")}"
  157. extensions.each do |meth|
  158. msg = "plugin_#{meth}_options"
  159. send msg, opts, options if self.respond_to?(msg)
  160. end
  161. end
  162. begin
  163. opts.parse! args
  164. rescue OptionParser::InvalidOption => e
  165. puts
  166. puts e
  167. puts
  168. puts opts
  169. exit 1
  170. end
  171. orig_args -= args
  172. end
  173. unless options[:seed] then
  174. srand
  175. options[:seed] = (ENV["SEED"] || srand).to_i % 0xFFFF
  176. orig_args << "--seed" << options[:seed].to_s
  177. end
  178. srand options[:seed]
  179. options[:args] = orig_args.map { |s|
  180. s =~ /[\s|&<>$()]/ ? s.inspect : s
  181. }.join " "
  182. options
  183. end
  184. 1 def self.filter_backtrace bt # :nodoc:
  185. result = backtrace_filter.filter bt
  186. result = bt.dup if result.empty?
  187. result
  188. end
  189. ##
  190. # Represents anything "runnable", like Test, Spec, Benchmark, or
  191. # whatever you can dream up.
  192. #
  193. # Subclasses of this are automatically registered and available in
  194. # Runnable.runnables.
  195. 1 class Runnable
  196. ##
  197. # Number of assertions executed in this run.
  198. 1 attr_accessor :assertions
  199. ##
  200. # An assertion raised during the run, if any.
  201. 1 attr_accessor :failures
  202. ##
  203. # The time it took to run.
  204. 1 attr_accessor :time
  205. 1 def time_it # :nodoc:
  206. t0 = Minitest.clock_time
  207. yield
  208. ensure
  209. self.time = Minitest.clock_time - t0
  210. end
  211. ##
  212. # Name of the run.
  213. 1 def name
  214. @NAME
  215. end
  216. ##
  217. # Set the name of the run.
  218. 1 def name= o
  219. @NAME = o
  220. end
  221. ##
  222. # Returns all instance methods matching the pattern +re+.
  223. 1 def self.methods_matching re
  224. public_instance_methods(true).grep(re).map(&:to_s)
  225. end
  226. 1 def self.reset # :nodoc:
  227. 1 @@runnables = []
  228. end
  229. 1 reset
  230. ##
  231. # Responsible for running all runnable methods in a given class,
  232. # each in its own instance. Each instance is passed to the
  233. # reporter to record.
  234. 1 def self.run reporter, options = {}
  235. filter = options[:filter] || "/./"
  236. filter = Regexp.new $1 if filter.is_a?(String) && filter =~ %r%/(.*)/%
  237. filtered_methods = self.runnable_methods.find_all { |m|
  238. filter === m || filter === "#{self}##{m}"
  239. }
  240. exclude = options[:exclude]
  241. exclude = Regexp.new $1 if exclude =~ %r%/(.*)/%
  242. filtered_methods.delete_if { |m|
  243. exclude === m || exclude === "#{self}##{m}"
  244. }
  245. return if filtered_methods.empty?
  246. with_info_handler reporter do
  247. filtered_methods.each do |method_name|
  248. run_one_method self, method_name, reporter
  249. end
  250. end
  251. end
  252. ##
  253. # Runs a single method and has the reporter record the result.
  254. # This was considered internal API but is factored out of run so
  255. # that subclasses can specialize the running of an individual
  256. # test. See Minitest::ParallelTest::ClassMethods for an example.
  257. 1 def self.run_one_method klass, method_name, reporter
  258. reporter.prerecord klass, method_name
  259. reporter.record Minitest.run_one_method(klass, method_name)
  260. end
  261. 1 def self.with_info_handler reporter, &block # :nodoc:
  262. handler = lambda do
  263. unless reporter.passed? then
  264. warn "Current results:"
  265. warn ""
  266. warn reporter.reporters.first
  267. warn ""
  268. end
  269. end
  270. on_signal ::Minitest.info_signal, handler, &block
  271. end
  272. 1 SIGNALS = Signal.list # :nodoc:
  273. 1 def self.on_signal name, action # :nodoc:
  274. supported = SIGNALS[name]
  275. old_trap = trap name do
  276. old_trap.call if old_trap.respond_to? :call
  277. action.call
  278. end if supported
  279. yield
  280. ensure
  281. trap name, old_trap if supported
  282. end
  283. ##
  284. # Each subclass of Runnable is responsible for overriding this
  285. # method to return all runnable methods. See #methods_matching.
  286. 1 def self.runnable_methods
  287. raise NotImplementedError, "subclass responsibility"
  288. end
  289. ##
  290. # Returns all subclasses of Runnable.
  291. 1 def self.runnables
  292. 9 @@runnables
  293. end
  294. 1 @@marshal_dump_warned = false
  295. 1 def marshal_dump # :nodoc:
  296. unless @@marshal_dump_warned then
  297. warn ["Minitest::Runnable#marshal_dump is deprecated.",
  298. "You might be violating internals. From", caller.first].join " "
  299. @@marshal_dump_warned = true
  300. end
  301. [self.name, self.failures, self.assertions, self.time]
  302. end
  303. 1 def marshal_load ary # :nodoc:
  304. self.name, self.failures, self.assertions, self.time = ary
  305. end
  306. 1 def failure # :nodoc:
  307. self.failures.first
  308. end
  309. 1 def initialize name # :nodoc:
  310. self.name = name
  311. self.failures = []
  312. self.assertions = 0
  313. end
  314. ##
  315. # Runs a single method. Needs to return self.
  316. 1 def run
  317. raise NotImplementedError, "subclass responsibility"
  318. end
  319. ##
  320. # Did this run pass?
  321. #
  322. # Note: skipped runs are not considered passing, but they don't
  323. # cause the process to exit non-zero.
  324. 1 def passed?
  325. raise NotImplementedError, "subclass responsibility"
  326. end
  327. ##
  328. # Returns a single character string to print based on the result
  329. # of the run. One of <tt>"."</tt>, <tt>"F"</tt>,
  330. # <tt>"E"</tt> or <tt>"S"</tt>.
  331. 1 def result_code
  332. raise NotImplementedError, "subclass responsibility"
  333. end
  334. ##
  335. # Was this run skipped? See #passed? for more information.
  336. 1 def skipped?
  337. raise NotImplementedError, "subclass responsibility"
  338. end
  339. end
  340. ##
  341. # Shared code for anything that can get passed to a Reporter. See
  342. # Minitest::Test & Minitest::Result.
  343. 1 module Reportable
  344. ##
  345. # Did this run pass?
  346. #
  347. # Note: skipped runs are not considered passing, but they don't
  348. # cause the process to exit non-zero.
  349. 1 def passed?
  350. not self.failure
  351. end
  352. ##
  353. # The location identifier of this test. Depends on a method
  354. # existing called class_name.
  355. 1 def location
  356. loc = " [#{self.failure.location}]" unless passed? or error?
  357. "#{self.class_name}##{self.name}#{loc}"
  358. end
  359. 1 def class_name # :nodoc:
  360. raise NotImplementedError, "subclass responsibility"
  361. end
  362. ##
  363. # Returns ".", "F", or "E" based on the result of the run.
  364. 1 def result_code
  365. self.failure and self.failure.result_code or "."
  366. end
  367. ##
  368. # Was this run skipped?
  369. 1 def skipped?
  370. self.failure and Skip === self.failure
  371. end
  372. ##
  373. # Did this run error?
  374. 1 def error?
  375. self.failures.any? { |f| UnexpectedError === f }
  376. end
  377. end
  378. ##
  379. # This represents a test result in a clean way that can be
  380. # marshalled over a wire. Tests can do anything they want to the
  381. # test instance and can create conditions that cause Marshal.dump to
  382. # blow up. By using Result.from(a_test) you can be reasonably sure
  383. # that the test result can be marshalled.
  384. 1 class Result < Runnable
  385. 1 include Minitest::Reportable
  386. 1 undef_method :marshal_dump
  387. 1 undef_method :marshal_load
  388. ##
  389. # The class name of the test result.
  390. 1 attr_accessor :klass
  391. ##
  392. # The location of the test method.
  393. 1 attr_accessor :source_location
  394. ##
  395. # Create a new test result from a Runnable instance.
  396. 1 def self.from runnable
  397. o = runnable
  398. r = self.new o.name
  399. r.klass = o.class.name
  400. r.assertions = o.assertions
  401. r.failures = o.failures.dup
  402. r.time = o.time
  403. r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
  404. r
  405. end
  406. 1 def class_name # :nodoc:
  407. self.klass # for Minitest::Reportable
  408. end
  409. 1 def to_s # :nodoc:
  410. return location if passed? and not skipped?
  411. failures.map { |failure|
  412. "#{failure.result_label}:\n#{self.location}:\n#{failure.message}\n"
  413. }.join "\n"
  414. end
  415. end
  416. ##
  417. # Defines the API for Reporters. Subclass this and override whatever
  418. # you want. Go nuts.
  419. 1 class AbstractReporter
  420. 1 include Mutex_m
  421. ##
  422. # Starts reporting on the run.
  423. 1 def start
  424. end
  425. ##
  426. # About to start running a test. This allows a reporter to show
  427. # that it is starting or that we are in the middle of a test run.
  428. 1 def prerecord klass, name
  429. end
  430. ##
  431. # Output and record the result of the test. Call
  432. # {result#result_code}[rdoc-ref:Runnable#result_code] to get the
  433. # result character string. Stores the result of the run if the run
  434. # did not pass.
  435. 1 def record result
  436. end
  437. ##
  438. # Outputs the summary of the run.
  439. 1 def report
  440. end
  441. ##
  442. # Did this run pass?
  443. 1 def passed?
  444. true
  445. end
  446. end
  447. 1 class Reporter < AbstractReporter # :nodoc:
  448. ##
  449. # The IO used to report.
  450. 1 attr_accessor :io
  451. ##
  452. # Command-line options for this run.
  453. 1 attr_accessor :options
  454. 1 def initialize io = $stdout, options = {} # :nodoc:
  455. super()
  456. self.io = io
  457. self.options = options
  458. end
  459. end
  460. ##
  461. # A very simple reporter that prints the "dots" during the run.
  462. #
  463. # This is added to the top-level CompositeReporter at the start of
  464. # the run. If you want to change the output of minitest via a
  465. # plugin, pull this out of the composite and replace it with your
  466. # own.
  467. 1 class ProgressReporter < Reporter
  468. 1 def prerecord klass, name #:nodoc:
  469. if options[:verbose] then
  470. io.print "%s#%s = " % [klass.name, name]
  471. io.flush
  472. end
  473. end
  474. 1 def record result # :nodoc:
  475. io.print "%.2f s = " % [result.time] if options[:verbose]
  476. io.print result.result_code
  477. io.puts if options[:verbose]
  478. end
  479. end
  480. ##
  481. # A reporter that gathers statistics about a test run. Does not do
  482. # any IO because meant to be used as a parent class for a reporter
  483. # that does.
  484. #
  485. # If you want to create an entirely different type of output (eg,
  486. # CI, HTML, etc), this is the place to start.
  487. #
  488. # Example:
  489. #
  490. # class JenkinsCIReporter < StatisticsReporter
  491. # def report
  492. # super # Needed to calculate some statistics
  493. #
  494. # print "<testsuite "
  495. # print "tests='#{count}' "
  496. # print "failures='#{failures}' "
  497. # # Remaining XML...
  498. # end
  499. # end
  500. 1 class StatisticsReporter < Reporter
  501. ##
  502. # Total number of assertions.
  503. 1 attr_accessor :assertions
  504. ##
  505. # Total number of test cases.
  506. 1 attr_accessor :count
  507. ##
  508. # An +Array+ of test cases that failed or were skipped.
  509. 1 attr_accessor :results
  510. ##
  511. # Time the test run started. If available, the monotonic clock is
  512. # used and this is a +Float+, otherwise it's an instance of
  513. # +Time+.
  514. 1 attr_accessor :start_time
  515. ##
  516. # Test run time. If available, the monotonic clock is used and
  517. # this is a +Float+, otherwise it's an instance of +Time+.
  518. 1 attr_accessor :total_time
  519. ##
  520. # Total number of tests that failed.
  521. 1 attr_accessor :failures
  522. ##
  523. # Total number of tests that erred.
  524. 1 attr_accessor :errors
  525. ##
  526. # Total number of tests that where skipped.
  527. 1 attr_accessor :skips
  528. 1 def initialize io = $stdout, options = {} # :nodoc:
  529. super
  530. self.assertions = 0
  531. self.count = 0
  532. self.results = []
  533. self.start_time = nil
  534. self.total_time = nil
  535. self.failures = nil
  536. self.errors = nil
  537. self.skips = nil
  538. end
  539. 1 def passed? # :nodoc:
  540. results.all?(&:skipped?)
  541. end
  542. 1 def start # :nodoc:
  543. self.start_time = Minitest.clock_time
  544. end
  545. 1 def record result # :nodoc:
  546. self.count += 1
  547. self.assertions += result.assertions
  548. results << result if not result.passed? or result.skipped?
  549. end
  550. ##
  551. # Report on the tracked statistics.
  552. 1 def report
  553. aggregate = results.group_by { |r| r.failure.class }
  554. aggregate.default = [] # dumb. group_by should provide this
  555. self.total_time = Minitest.clock_time - start_time
  556. self.failures = aggregate[Assertion].size
  557. self.errors = aggregate[UnexpectedError].size
  558. self.skips = aggregate[Skip].size
  559. end
  560. end
  561. ##
  562. # A reporter that prints the header, summary, and failure details at
  563. # the end of the run.
  564. #
  565. # This is added to the top-level CompositeReporter at the start of
  566. # the run. If you want to change the output of minitest via a
  567. # plugin, pull this out of the composite and replace it with your
  568. # own.
  569. 1 class SummaryReporter < StatisticsReporter
  570. # :stopdoc:
  571. 1 attr_accessor :sync
  572. 1 attr_accessor :old_sync
  573. # :startdoc:
  574. 1 def start # :nodoc:
  575. super
  576. io.puts "Run options: #{options[:args]}"
  577. io.puts
  578. io.puts "# Running:"
  579. io.puts
  580. self.sync = io.respond_to? :"sync=" # stupid emacs
  581. self.old_sync, io.sync = io.sync, true if self.sync
  582. end
  583. 1 def report # :nodoc:
  584. super
  585. io.sync = self.old_sync
  586. io.puts unless options[:verbose] # finish the dots
  587. io.puts
  588. io.puts statistics
  589. aggregated_results io
  590. io.puts summary
  591. end
  592. 1 def statistics # :nodoc:
  593. "Finished in %.6fs, %.4f runs/s, %.4f assertions/s." %
  594. [total_time, count / total_time, assertions / total_time]
  595. end
  596. 1 def aggregated_results io # :nodoc:
  597. filtered_results = results.dup
  598. filtered_results.reject!(&:skipped?) unless options[:verbose]
  599. filtered_results.each_with_index { |result, i|
  600. io.puts "\n%3d) %s" % [i+1, result]
  601. }
  602. io.puts
  603. io
  604. end
  605. 1 def to_s # :nodoc:
  606. aggregated_results(StringIO.new(binary_string)).string
  607. end
  608. 1 def summary # :nodoc:
  609. extra = ""
  610. extra = "\n\nYou have skipped tests. Run with --verbose for details." if
  611. results.any?(&:skipped?) unless options[:verbose] or ENV["MT_NO_SKIP_MSG"]
  612. "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
  613. [count, assertions, failures, errors, skips, extra]
  614. end
  615. 1 private
  616. 1 if '<3'.respond_to? :b
  617. 1 def binary_string; ''.b; end
  618. else
  619. def binary_string; ''.force_encoding(Encoding::ASCII_8BIT); end
  620. end
  621. end
  622. ##
  623. # Dispatch to multiple reporters as one.
  624. 1 class CompositeReporter < AbstractReporter
  625. ##
  626. # The list of reporters to dispatch to.
  627. 1 attr_accessor :reporters
  628. 1 def initialize *reporters # :nodoc:
  629. super()
  630. self.reporters = reporters
  631. end
  632. 1 def io # :nodoc:
  633. reporters.first.io
  634. end
  635. ##
  636. # Add another reporter to the mix.
  637. 1 def << reporter
  638. self.reporters << reporter
  639. end
  640. 1 def passed? # :nodoc:
  641. self.reporters.all?(&:passed?)
  642. end
  643. 1 def start # :nodoc:
  644. self.reporters.each(&:start)
  645. end
  646. 1 def prerecord klass, name # :nodoc:
  647. self.reporters.each do |reporter|
  648. # TODO: remove conditional for minitest 6
  649. reporter.prerecord klass, name if reporter.respond_to? :prerecord
  650. end
  651. end
  652. 1 def record result # :nodoc:
  653. self.reporters.each do |reporter|
  654. reporter.record result
  655. end
  656. end
  657. 1 def report # :nodoc:
  658. self.reporters.each(&:report)
  659. end
  660. end
  661. ##
  662. # Represents run failures.
  663. 1 class Assertion < Exception
  664. 1 def error # :nodoc:
  665. self
  666. end
  667. ##
  668. # Where was this run before an assertion was raised?
  669. 1 def location
  670. last_before_assertion = ""
  671. self.backtrace.reverse_each do |s|
  672. break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
  673. last_before_assertion = s
  674. end
  675. last_before_assertion.sub(/:in .*$/, "")
  676. end
  677. 1 def result_code # :nodoc:
  678. result_label[0, 1]
  679. end
  680. 1 def result_label # :nodoc:
  681. "Failure"
  682. end
  683. end
  684. ##
  685. # Assertion raised when skipping a run.
  686. 1 class Skip < Assertion
  687. 1 def result_label # :nodoc:
  688. "Skipped"
  689. end
  690. end
  691. ##
  692. # Assertion wrapping an unexpected error that was raised during a run.
  693. 1 class UnexpectedError < Assertion
  694. # TODO: figure out how to use `cause` instead
  695. 1 attr_accessor :error # :nodoc:
  696. 1 def initialize error # :nodoc:
  697. super "Unexpected exception"
  698. self.error = error
  699. end
  700. 1 def backtrace # :nodoc:
  701. self.error.backtrace
  702. end
  703. 1 def message # :nodoc:
  704. bt = Minitest.filter_backtrace(self.backtrace).join "\n "
  705. "#{self.error.class}: #{self.error.message}\n #{bt}"
  706. end
  707. 1 def result_label # :nodoc:
  708. "Error"
  709. end
  710. end
  711. ##
  712. # Provides a simple set of guards that you can use in your tests
  713. # to skip execution if it is not applicable. These methods are
  714. # mixed into Test as both instance and class methods so you
  715. # can use them inside or outside of the test methods.
  716. #
  717. # def test_something_for_mri
  718. # skip "bug 1234" if jruby?
  719. # # ...
  720. # end
  721. #
  722. # if windows? then
  723. # # ... lots of test methods ...
  724. # end
  725. 1 module Guard
  726. ##
  727. # Is this running on jruby?
  728. 1 def jruby? platform = RUBY_PLATFORM
  729. "java" == platform
  730. end
  731. ##
  732. # Is this running on maglev?
  733. 1 def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
  734. where = Minitest.filter_backtrace(caller).first
  735. where = where.split(/:in /, 2).first # clean up noise
  736. warn "DEPRECATED: `maglev?` called from #{where}. This will fail in Minitest 6."
  737. "maglev" == platform
  738. end
  739. ##
  740. # Is this running on mri?
  741. 1 def mri? platform = RUBY_DESCRIPTION
  742. /^ruby/ =~ platform
  743. end
  744. ##
  745. # Is this running on macOS?
  746. 1 def osx? platform = RUBY_PLATFORM
  747. /darwin/ =~ platform
  748. end
  749. ##
  750. # Is this running on rubinius?
  751. 1 def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
  752. where = Minitest.filter_backtrace(caller).first
  753. where = where.split(/:in /, 2).first # clean up noise
  754. warn "DEPRECATED: `rubinius?` called from #{where}. This will fail in Minitest 6."
  755. "rbx" == platform
  756. end
  757. ##
  758. # Is this running on windows?
  759. 1 def windows? platform = RUBY_PLATFORM
  760. /mswin|mingw/ =~ platform
  761. end
  762. end
  763. ##
  764. # The standard backtrace filter for minitest.
  765. #
  766. # See Minitest.backtrace_filter=.
  767. 1 class BacktraceFilter
  768. 1 MT_RE = %r%lib/minitest% #:nodoc:
  769. ##
  770. # Filter +bt+ to something useful. Returns the whole thing if
  771. # $DEBUG (ruby) or $MT_DEBUG (env).
  772. 1 def filter bt
  773. return ["No backtrace"] unless bt
  774. return bt.dup if $DEBUG || ENV["MT_DEBUG"]
  775. new_bt = bt.take_while { |line| line !~ MT_RE }
  776. new_bt = bt.select { |line| line !~ MT_RE } if new_bt.empty?
  777. new_bt = bt.dup if new_bt.empty?
  778. new_bt
  779. end
  780. end
  781. 1 self.backtrace_filter = BacktraceFilter.new
  782. 1 def self.run_one_method klass, method_name # :nodoc:
  783. result = klass.new(method_name).run
  784. raise "#{klass}#run _must_ return a Result" unless Result === result
  785. result
  786. end
  787. # :stopdoc:
  788. 1 if defined? Process::CLOCK_MONOTONIC # :nodoc:
  789. 1 def self.clock_time
  790. Process.clock_gettime Process::CLOCK_MONOTONIC
  791. end
  792. else
  793. def self.clock_time
  794. Time.now
  795. end
  796. end
  797. 1 class Runnable # re-open
  798. 1 def self.inherited klass # :nodoc:
  799. 9 self.runnables << klass
  800. 9 super
  801. end
  802. end
  803. # :startdoc:
  804. end
  805. 1 require "minitest/test"

vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/assertions.rb

20.69% lines covered

290 relevant lines. 60 lines covered and 230 lines missed.
    
  1. # encoding: UTF-8
  2. 1 require "rbconfig"
  3. 1 require "tempfile"
  4. 1 require "stringio"
  5. 1 module Minitest
  6. ##
  7. # Minitest Assertions. All assertion methods accept a +msg+ which is
  8. # printed if the assertion fails.
  9. #
  10. # Protocol: Nearly everything here boils up to +assert+, which
  11. # expects to be able to increment an instance accessor named
  12. # +assertions+. This is not provided by Assertions and must be
  13. # provided by the thing including Assertions. See Minitest::Runnable
  14. # for an example.
  15. 1 module Assertions
  16. 1 UNDEFINED = Object.new # :nodoc:
  17. 1 def UNDEFINED.inspect # :nodoc:
  18. "UNDEFINED" # again with the rdoc bugs... :(
  19. end
  20. ##
  21. # Returns the diff command to use in #diff. Tries to intelligently
  22. # figure out what diff to use.
  23. 1 def self.diff
  24. return @diff if defined? @diff
  25. @diff = if (RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ &&
  26. system("diff.exe", __FILE__, __FILE__)) then
  27. "diff.exe -u"
  28. elsif system("gdiff", __FILE__, __FILE__)
  29. "gdiff -u" # solaris and kin suck
  30. elsif system("diff", __FILE__, __FILE__)
  31. "diff -u"
  32. else
  33. nil
  34. end
  35. end
  36. ##
  37. # Set the diff command to use in #diff.
  38. 1 def self.diff= o
  39. @diff = o
  40. end
  41. ##
  42. # Returns a diff between +exp+ and +act+. If there is no known
  43. # diff command or if it doesn't make sense to diff the output
  44. # (single line, short output), then it simply returns a basic
  45. # comparison between the two.
  46. #
  47. # See +things_to_diff+ for more info.
  48. 1 def diff exp, act
  49. result = nil
  50. expect, butwas = things_to_diff(exp, act)
  51. return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
  52. expect
  53. Tempfile.open("expect") do |a|
  54. a.puts expect
  55. a.flush
  56. Tempfile.open("butwas") do |b|
  57. b.puts butwas
  58. b.flush
  59. result = `#{Minitest::Assertions.diff} #{a.path} #{b.path}`
  60. result.sub!(/^\-\-\- .+/, "--- expected")
  61. result.sub!(/^\+\+\+ .+/, "+++ actual")
  62. if result.empty? then
  63. klass = exp.class
  64. result = [
  65. "No visible difference in the #{klass}#inspect output.\n",
  66. "You should look at the implementation of #== on ",
  67. "#{klass} or its members.\n",
  68. expect,
  69. ].join
  70. end
  71. end
  72. end
  73. result
  74. end
  75. ##
  76. # Returns things to diff [expect, butwas], or [nil, nil] if nothing to diff.
  77. #
  78. # Criterion:
  79. #
  80. # 1. Strings include newlines or escaped newlines, but not both.
  81. # 2. or: String lengths are > 30 characters.
  82. # 3. or: Strings are equal to each other (but maybe different encodings?).
  83. # 4. and: we found a diff executable.
  84. 1 def things_to_diff exp, act
  85. expect = mu_pp_for_diff exp
  86. butwas = mu_pp_for_diff act
  87. e1, e2 = expect.include?("\n"), expect.include?("\\n")
  88. b1, b2 = butwas.include?("\n"), butwas.include?("\\n")
  89. need_to_diff =
  90. (e1 ^ e2 ||
  91. b1 ^ b2 ||
  92. expect.size > 30 ||
  93. butwas.size > 30 ||
  94. expect == butwas) &&
  95. Minitest::Assertions.diff
  96. need_to_diff && [expect, butwas]
  97. end
  98. ##
  99. # This returns a human-readable version of +obj+. By default
  100. # #inspect is called. You can override this to use #pretty_inspect
  101. # if you want.
  102. #
  103. # See Minitest::Test.make_my_diffs_pretty!
  104. 1 def mu_pp obj
  105. s = obj.inspect
  106. if defined? Encoding then
  107. s = s.encode Encoding.default_external
  108. if String === obj && (obj.encoding != Encoding.default_external ||
  109. !obj.valid_encoding?) then
  110. enc = "# encoding: #{obj.encoding}"
  111. val = "# valid: #{obj.valid_encoding?}"
  112. s = "#{enc}\n#{val}\n#{s}"
  113. end
  114. end
  115. s
  116. end
  117. ##
  118. # This returns a diff-able more human-readable version of +obj+.
  119. # This differs from the regular mu_pp because it expands escaped
  120. # newlines and makes hex-values (like object_ids) generic. This
  121. # uses mu_pp to do the first pass and then cleans it up.
  122. 1 def mu_pp_for_diff obj
  123. str = mu_pp obj
  124. # both '\n' & '\\n' (_after_ mu_pp (aka inspect))
  125. single = !!str.match(/(?<!\\|^)\\n/)
  126. double = !!str.match(/(?<=\\|^)\\n/)
  127. process =
  128. if single ^ double then
  129. if single then
  130. lambda { |s| s == "\\n" ? "\n" : s } # unescape
  131. else
  132. lambda { |s| s == "\\\\n" ? "\\n\n" : s } # unescape a bit, add nls
  133. end
  134. else
  135. :itself # leave it alone
  136. end
  137. str.
  138. gsub(/\\?\\n/, &process).
  139. gsub(/:0x[a-fA-F0-9]{4,}/m, ":0xXXXXXX") # anonymize hex values
  140. end
  141. ##
  142. # Fails unless +test+ is truthy.
  143. 1 def assert test, msg = nil
  144. self.assertions += 1
  145. unless test then
  146. msg ||= "Expected #{mu_pp test} to be truthy."
  147. msg = msg.call if Proc === msg
  148. raise Minitest::Assertion, msg
  149. end
  150. true
  151. end
  152. 1 def _synchronize # :nodoc:
  153. yield
  154. end
  155. ##
  156. # Fails unless +obj+ is empty.
  157. 1 def assert_empty obj, msg = nil
  158. msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
  159. assert_respond_to obj, :empty?
  160. assert obj.empty?, msg
  161. end
  162. 1 E = "" # :nodoc:
  163. ##
  164. # Fails unless <tt>exp == act</tt> printing the difference between
  165. # the two, if possible.
  166. #
  167. # If there is no visible difference but the assertion fails, you
  168. # should suspect that your #== is buggy, or your inspect output is
  169. # missing crucial details. For nicer structural diffing, set
  170. # Minitest::Test.make_my_diffs_pretty!
  171. #
  172. # For floats use assert_in_delta.
  173. #
  174. # See also: Minitest::Assertions.diff
  175. 1 def assert_equal exp, act, msg = nil
  176. msg = message(msg, E) { diff exp, act }
  177. result = assert exp == act, msg
  178. if nil == exp then
  179. if Minitest::VERSION =~ /^6/ then
  180. refute_nil exp, "Use assert_nil if expecting nil."
  181. else
  182. where = Minitest.filter_backtrace(caller).first
  183. where = where.split(/:in /, 2).first # clean up noise
  184. warn "DEPRECATED: Use assert_nil if expecting nil from #{where}. This will fail in Minitest 6."
  185. end
  186. end
  187. result
  188. end
  189. ##
  190. # For comparing Floats. Fails unless +exp+ and +act+ are within +delta+
  191. # of each other.
  192. #
  193. # assert_in_delta Math::PI, (22.0 / 7.0), 0.01
  194. 1 def assert_in_delta exp, act, delta = 0.001, msg = nil
  195. n = (exp - act).abs
  196. msg = message(msg) {
  197. "Expected |#{exp} - #{act}| (#{n}) to be <= #{delta}"
  198. }
  199. assert delta >= n, msg
  200. end
  201. ##
  202. # For comparing Floats. Fails unless +exp+ and +act+ have a relative
  203. # error less than +epsilon+.
  204. 1 def assert_in_epsilon exp, act, epsilon = 0.001, msg = nil
  205. assert_in_delta exp, act, [exp.abs, act.abs].min * epsilon, msg
  206. end
  207. ##
  208. # Fails unless +collection+ includes +obj+.
  209. 1 def assert_includes collection, obj, msg = nil
  210. msg = message(msg) {
  211. "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
  212. }
  213. assert_respond_to collection, :include?
  214. assert collection.include?(obj), msg
  215. end
  216. ##
  217. # Fails unless +obj+ is an instance of +cls+.
  218. 1 def assert_instance_of cls, obj, msg = nil
  219. msg = message(msg) {
  220. "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}"
  221. }
  222. assert obj.instance_of?(cls), msg
  223. end
  224. ##
  225. # Fails unless +obj+ is a kind of +cls+.
  226. 1 def assert_kind_of cls, obj, msg = nil
  227. msg = message(msg) {
  228. "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
  229. assert obj.kind_of?(cls), msg
  230. end
  231. ##
  232. # Fails unless +matcher+ <tt>=~</tt> +obj+.
  233. 1 def assert_match matcher, obj, msg = nil
  234. msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" }
  235. assert_respond_to matcher, :"=~"
  236. matcher = Regexp.new Regexp.escape matcher if String === matcher
  237. assert matcher =~ obj, msg
  238. end
  239. ##
  240. # Fails unless +obj+ is nil
  241. 1 def assert_nil obj, msg = nil
  242. msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
  243. assert obj.nil?, msg
  244. end
  245. ##
  246. # For testing with binary operators. Eg:
  247. #
  248. # assert_operator 5, :<=, 4
  249. 1 def assert_operator o1, op, o2 = UNDEFINED, msg = nil
  250. return assert_predicate o1, op, msg if UNDEFINED == o2
  251. msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
  252. assert o1.__send__(op, o2), msg
  253. end
  254. ##
  255. # Fails if stdout or stderr do not output the expected results.
  256. # Pass in nil if you don't care about that streams output. Pass in
  257. # "" if you require it to be silent. Pass in a regexp if you want
  258. # to pattern match.
  259. #
  260. # assert_output(/hey/) { method_with_output }
  261. #
  262. # NOTE: this uses #capture_io, not #capture_subprocess_io.
  263. #
  264. # See also: #assert_silent
  265. 1 def assert_output stdout = nil, stderr = nil
  266. flunk "assert_output requires a block to capture output." unless
  267. block_given?
  268. out, err = capture_io do
  269. yield
  270. end
  271. err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
  272. out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
  273. y = send err_msg, stderr, err, "In stderr" if err_msg
  274. x = send out_msg, stdout, out, "In stdout" if out_msg
  275. (!stdout || x) && (!stderr || y)
  276. rescue Assertion
  277. raise
  278. rescue => e
  279. raise UnexpectedError, e
  280. end
  281. ##
  282. # Fails unless +path+ exists.
  283. 1 def assert_path_exists path, msg = nil
  284. msg = message(msg) { "Expected path '#{path}' to exist" }
  285. assert File.exist?(path), msg
  286. end
  287. ##
  288. # For testing with predicates. Eg:
  289. #
  290. # assert_predicate str, :empty?
  291. #
  292. # This is really meant for specs and is front-ended by assert_operator:
  293. #
  294. # str.must_be :empty?
  295. 1 def assert_predicate o1, op, msg = nil
  296. msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" }
  297. assert o1.__send__(op), msg
  298. end
  299. ##
  300. # Fails unless the block raises one of +exp+. Returns the
  301. # exception matched so you can check the message, attributes, etc.
  302. #
  303. # +exp+ takes an optional message on the end to help explain
  304. # failures and defaults to StandardError if no exception class is
  305. # passed. Eg:
  306. #
  307. # assert_raises(CustomError) { method_with_custom_error }
  308. #
  309. # With custom error message:
  310. #
  311. # assert_raises(CustomError, 'This should have raised CustomError') { method_with_custom_error }
  312. #
  313. # Using the returned object:
  314. #
  315. # error = assert_raises(CustomError) do
  316. # raise CustomError, 'This is really bad'
  317. # end
  318. #
  319. # assert_equal 'This is really bad', error.message
  320. 1 def assert_raises *exp
  321. flunk "assert_raises requires a block to capture errors." unless
  322. block_given?
  323. msg = "#{exp.pop}.\n" if String === exp.last
  324. exp << StandardError if exp.empty?
  325. begin
  326. yield
  327. rescue *exp => e
  328. pass # count assertion
  329. return e
  330. rescue Minitest::Assertion # incl Skip & UnexpectedError
  331. # don't count assertion
  332. raise
  333. rescue SignalException, SystemExit
  334. raise
  335. rescue Exception => e
  336. flunk proc {
  337. exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not")
  338. }
  339. end
  340. exp = exp.first if exp.size == 1
  341. flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised."
  342. end
  343. ##
  344. # Fails unless +obj+ responds to +meth+.
  345. 1 def assert_respond_to obj, meth, msg = nil
  346. msg = message(msg) {
  347. "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
  348. }
  349. assert obj.respond_to?(meth), msg
  350. end
  351. ##
  352. # Fails unless +exp+ and +act+ are #equal?
  353. 1 def assert_same exp, act, msg = nil
  354. msg = message(msg) {
  355. data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
  356. "Expected %s (oid=%d) to be the same as %s (oid=%d)" % data
  357. }
  358. assert exp.equal?(act), msg
  359. end
  360. ##
  361. # +send_ary+ is a receiver, message and arguments.
  362. #
  363. # Fails unless the call returns a true value
  364. 1 def assert_send send_ary, m = nil
  365. where = Minitest.filter_backtrace(caller).first
  366. where = where.split(/:in /, 2).first # clean up noise
  367. warn "DEPRECATED: assert_send. From #{where}"
  368. recv, msg, *args = send_ary
  369. m = message(m) {
  370. "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
  371. assert recv.__send__(msg, *args), m
  372. end
  373. ##
  374. # Fails if the block outputs anything to stderr or stdout.
  375. #
  376. # See also: #assert_output
  377. 1 def assert_silent
  378. assert_output "", "" do
  379. yield
  380. end
  381. end
  382. ##
  383. # Fails unless the block throws +sym+
  384. 1 def assert_throws sym, msg = nil
  385. default = "Expected #{mu_pp(sym)} to have been thrown"
  386. caught = true
  387. catch(sym) do
  388. begin
  389. yield
  390. rescue ThreadError => e # wtf?!? 1.8 + threads == suck
  391. default += ", not \:#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
  392. rescue ArgumentError => e # 1.9 exception
  393. raise e unless e.message.include?("uncaught throw")
  394. default += ", not #{e.message.split(/ /).last}"
  395. rescue NameError => e # 1.8 exception
  396. raise e unless e.name == sym
  397. default += ", not #{e.name.inspect}"
  398. end
  399. caught = false
  400. end
  401. assert caught, message(msg) { default }
  402. rescue Assertion
  403. raise
  404. rescue => e
  405. raise UnexpectedError, e
  406. end
  407. ##
  408. # Captures $stdout and $stderr into strings:
  409. #
  410. # out, err = capture_io do
  411. # puts "Some info"
  412. # warn "You did a bad thing"
  413. # end
  414. #
  415. # assert_match %r%info%, out
  416. # assert_match %r%bad%, err
  417. #
  418. # NOTE: For efficiency, this method uses StringIO and does not
  419. # capture IO for subprocesses. Use #capture_subprocess_io for
  420. # that.
  421. 1 def capture_io
  422. _synchronize do
  423. begin
  424. captured_stdout, captured_stderr = StringIO.new, StringIO.new
  425. orig_stdout, orig_stderr = $stdout, $stderr
  426. $stdout, $stderr = captured_stdout, captured_stderr
  427. yield
  428. return captured_stdout.string, captured_stderr.string
  429. ensure
  430. $stdout = orig_stdout
  431. $stderr = orig_stderr
  432. end
  433. end
  434. end
  435. ##
  436. # Captures $stdout and $stderr into strings, using Tempfile to
  437. # ensure that subprocess IO is captured as well.
  438. #
  439. # out, err = capture_subprocess_io do
  440. # system "echo Some info"
  441. # system "echo You did a bad thing 1>&2"
  442. # end
  443. #
  444. # assert_match %r%info%, out
  445. # assert_match %r%bad%, err
  446. #
  447. # NOTE: This method is approximately 10x slower than #capture_io so
  448. # only use it when you need to test the output of a subprocess.
  449. 1 def capture_subprocess_io
  450. _synchronize do
  451. begin
  452. require "tempfile"
  453. captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
  454. orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
  455. $stdout.reopen captured_stdout
  456. $stderr.reopen captured_stderr
  457. yield
  458. $stdout.rewind
  459. $stderr.rewind
  460. return captured_stdout.read, captured_stderr.read
  461. ensure
  462. captured_stdout.unlink
  463. captured_stderr.unlink
  464. $stdout.reopen orig_stdout
  465. $stderr.reopen orig_stderr
  466. orig_stdout.close
  467. orig_stderr.close
  468. captured_stdout.close
  469. captured_stderr.close
  470. end
  471. end
  472. end
  473. ##
  474. # Returns details for exception +e+
  475. 1 def exception_details e, msg
  476. [
  477. "#{msg}",
  478. "Class: <#{e.class}>",
  479. "Message: <#{e.message.inspect}>",
  480. "---Backtrace---",
  481. "#{Minitest.filter_backtrace(e.backtrace).join("\n")}",
  482. "---------------",
  483. ].join "\n"
  484. end
  485. ##
  486. # Fails after a given date (in the local time zone). This allows
  487. # you to put time-bombs in your tests if you need to keep
  488. # something around until a later date lest you forget about it.
  489. 1 def fail_after y,m,d,msg
  490. flunk msg if Time.now > Time.local(y, m, d)
  491. end
  492. ##
  493. # Fails with +msg+.
  494. 1 def flunk msg = nil
  495. msg ||= "Epic Fail!"
  496. assert false, msg
  497. end
  498. ##
  499. # Returns a proc that will output +msg+ along with the default message.
  500. 1 def message msg = nil, ending = nil, &default
  501. proc {
  502. msg = msg.call.chomp(".") if Proc === msg
  503. custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
  504. "#{custom_message}#{default.call}#{ending || "."}"
  505. }
  506. end
  507. ##
  508. # used for counting assertions
  509. 1 def pass _msg = nil
  510. assert true
  511. end
  512. ##
  513. # Fails if +test+ is truthy.
  514. 1 def refute test, msg = nil
  515. msg ||= message { "Expected #{mu_pp(test)} to not be truthy" }
  516. assert !test, msg
  517. end
  518. ##
  519. # Fails if +obj+ is empty.
  520. 1 def refute_empty obj, msg = nil
  521. msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
  522. assert_respond_to obj, :empty?
  523. refute obj.empty?, msg
  524. end
  525. ##
  526. # Fails if <tt>exp == act</tt>.
  527. #
  528. # For floats use refute_in_delta.
  529. 1 def refute_equal exp, act, msg = nil
  530. msg = message(msg) {
  531. "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
  532. }
  533. refute exp == act, msg
  534. end
  535. ##
  536. # For comparing Floats. Fails if +exp+ is within +delta+ of +act+.
  537. #
  538. # refute_in_delta Math::PI, (22.0 / 7.0)
  539. 1 def refute_in_delta exp, act, delta = 0.001, msg = nil
  540. n = (exp - act).abs
  541. msg = message(msg) {
  542. "Expected |#{exp} - #{act}| (#{n}) to not be <= #{delta}"
  543. }
  544. refute delta >= n, msg
  545. end
  546. ##
  547. # For comparing Floats. Fails if +exp+ and +act+ have a relative error
  548. # less than +epsilon+.
  549. 1 def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
  550. refute_in_delta a, b, a * epsilon, msg
  551. end
  552. ##
  553. # Fails if +collection+ includes +obj+.
  554. 1 def refute_includes collection, obj, msg = nil
  555. msg = message(msg) {
  556. "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
  557. }
  558. assert_respond_to collection, :include?
  559. refute collection.include?(obj), msg
  560. end
  561. ##
  562. # Fails if +obj+ is an instance of +cls+.
  563. 1 def refute_instance_of cls, obj, msg = nil
  564. msg = message(msg) {
  565. "Expected #{mu_pp(obj)} to not be an instance of #{cls}"
  566. }
  567. refute obj.instance_of?(cls), msg
  568. end
  569. ##
  570. # Fails if +obj+ is a kind of +cls+.
  571. 1 def refute_kind_of cls, obj, msg = nil
  572. msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
  573. refute obj.kind_of?(cls), msg
  574. end
  575. ##
  576. # Fails if +matcher+ <tt>=~</tt> +obj+.
  577. 1 def refute_match matcher, obj, msg = nil
  578. msg = message(msg) { "Expected #{mu_pp matcher} to not match #{mu_pp obj}" }
  579. assert_respond_to matcher, :"=~"
  580. matcher = Regexp.new Regexp.escape matcher if String === matcher
  581. refute matcher =~ obj, msg
  582. end
  583. ##
  584. # Fails if +obj+ is nil.
  585. 1 def refute_nil obj, msg = nil
  586. msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
  587. refute obj.nil?, msg
  588. end
  589. ##
  590. # Fails if +o1+ is not +op+ +o2+. Eg:
  591. #
  592. # refute_operator 1, :>, 2 #=> pass
  593. # refute_operator 1, :<, 2 #=> fail
  594. 1 def refute_operator o1, op, o2 = UNDEFINED, msg = nil
  595. return refute_predicate o1, op, msg if UNDEFINED == o2
  596. msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}" }
  597. refute o1.__send__(op, o2), msg
  598. end
  599. ##
  600. # Fails if +path+ exists.
  601. 1 def refute_path_exists path, msg = nil
  602. msg = message(msg) { "Expected path '#{path}' to not exist" }
  603. refute File.exist?(path), msg
  604. end
  605. ##
  606. # For testing with predicates.
  607. #
  608. # refute_predicate str, :empty?
  609. #
  610. # This is really meant for specs and is front-ended by refute_operator:
  611. #
  612. # str.wont_be :empty?
  613. 1 def refute_predicate o1, op, msg = nil
  614. msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" }
  615. refute o1.__send__(op), msg
  616. end
  617. ##
  618. # Fails if +obj+ responds to the message +meth+.
  619. 1 def refute_respond_to obj, meth, msg = nil
  620. msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
  621. refute obj.respond_to?(meth), msg
  622. end
  623. ##
  624. # Fails if +exp+ is the same (by object identity) as +act+.
  625. 1 def refute_same exp, act, msg = nil
  626. msg = message(msg) {
  627. data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
  628. "Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data
  629. }
  630. refute exp.equal?(act), msg
  631. end
  632. ##
  633. # Skips the current run. If run in verbose-mode, the skipped run
  634. # gets listed at the end of the run but doesn't cause a failure
  635. # exit code.
  636. 1 def skip msg = nil, bt = caller
  637. msg ||= "Skipped, no message given"
  638. @skip = true
  639. raise Minitest::Skip, msg, bt
  640. end
  641. ##
  642. # Skips the current run until a given date (in the local time
  643. # zone). This allows you to put some fixes on hold until a later
  644. # date, but still holds you accountable and prevents you from
  645. # forgetting it.
  646. 1 def skip_until y,m,d,msg
  647. skip msg if Time.now < Time.local(y, m, d)
  648. where = caller.first.split(/:/, 3).first(2).join ":"
  649. warn "Stale skip_until %p at %s" % [msg, where]
  650. end
  651. ##
  652. # Was this testcase skipped? Meant for #teardown.
  653. 1 def skipped?
  654. defined?(@skip) and @skip
  655. end
  656. end
  657. end

vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/parallel.rb

57.14% lines covered

28 relevant lines. 16 lines covered and 12 lines missed.
    
  1. 1 module Minitest
  2. 1 module Parallel #:nodoc:
  3. ##
  4. # The engine used to run multiple tests in parallel.
  5. 1 class Executor
  6. ##
  7. # The size of the pool of workers.
  8. 1 attr_reader :size
  9. ##
  10. # Create a parallel test executor of with +size+ workers.
  11. 1 def initialize size
  12. 1 @size = size
  13. 1 @queue = Queue.new
  14. 1 @pool = nil
  15. end
  16. ##
  17. # Start the executor
  18. 1 def start
  19. @pool = size.times.map {
  20. Thread.new(@queue) do |queue|
  21. Thread.current.abort_on_exception = true
  22. while (job = queue.pop)
  23. klass, method, reporter = job
  24. reporter.synchronize { reporter.prerecord klass, method }
  25. result = Minitest.run_one_method klass, method
  26. reporter.synchronize { reporter.record result }
  27. end
  28. end
  29. }
  30. end
  31. ##
  32. # Add a job to the queue
  33. 1 def << work; @queue << work; end
  34. ##
  35. # Shuts down the pool of workers by signalling them to quit and
  36. # waiting for them all to finish what they're currently working
  37. # on.
  38. 1 def shutdown
  39. size.times { @queue << nil }
  40. @pool.each(&:join)
  41. end
  42. end
  43. 1 module Test # :nodoc:
  44. 1 def _synchronize; Minitest::Test.io_lock.synchronize { yield }; end # :nodoc:
  45. 1 module ClassMethods # :nodoc:
  46. 1 def run_one_method klass, method_name, reporter
  47. Minitest.parallel_executor << [klass, method_name, reporter]
  48. end
  49. 1 def test_order
  50. :parallel
  51. end
  52. end
  53. end
  54. end
  55. end

vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/test.rb

48.39% lines covered

62 relevant lines. 30 lines covered and 32 lines missed.
    
  1. 1 require "minitest" unless defined? Minitest::Runnable
  2. 1 module Minitest
  3. ##
  4. # Subclass Test to create your own tests. Typically you'll want a
  5. # Test subclass per implementation class.
  6. #
  7. # See Minitest::Assertions
  8. 1 class Test < Runnable
  9. 1 require "minitest/assertions"
  10. 1 include Minitest::Assertions
  11. 1 include Minitest::Reportable
  12. 1 def class_name # :nodoc:
  13. self.class.name # for Minitest::Reportable
  14. end
  15. 1 PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, SystemExit] # :nodoc:
  16. # :stopdoc:
  17. 2 class << self; attr_accessor :io_lock; end
  18. 1 self.io_lock = Mutex.new
  19. # :startdoc:
  20. ##
  21. # Call this at the top of your tests when you absolutely
  22. # positively need to have ordered tests. In doing so, you're
  23. # admitting that you suck and your tests are weak.
  24. 1 def self.i_suck_and_my_tests_are_order_dependent!
  25. class << self
  26. undef_method :test_order if method_defined? :test_order
  27. define_method :test_order do :alpha end
  28. end
  29. end
  30. ##
  31. # Make diffs for this Test use #pretty_inspect so that diff
  32. # in assert_equal can have more details. NOTE: this is much slower
  33. # than the regular inspect but much more usable for complex
  34. # objects.
  35. 1 def self.make_my_diffs_pretty!
  36. require "pp"
  37. define_method :mu_pp, &:pretty_inspect
  38. end
  39. ##
  40. # Call this at the top of your tests when you want to run your
  41. # tests in parallel. In doing so, you're admitting that you rule
  42. # and your tests are awesome.
  43. 1 def self.parallelize_me!
  44. include Minitest::Parallel::Test
  45. extend Minitest::Parallel::Test::ClassMethods
  46. end
  47. ##
  48. # Returns all instance methods starting with "test_". Based on
  49. # #test_order, the methods are either sorted, randomized
  50. # (default), or run in parallel.
  51. 1 def self.runnable_methods
  52. methods = methods_matching(/^test_/)
  53. case self.test_order
  54. when :random, :parallel then
  55. max = methods.size
  56. methods.sort.sort_by { rand max }
  57. when :alpha, :sorted then
  58. methods.sort
  59. else
  60. raise "Unknown test_order: #{self.test_order.inspect}"
  61. end
  62. end
  63. ##
  64. # Defines the order to run tests (:random by default). Override
  65. # this or use a convenience method to change it for your tests.
  66. 1 def self.test_order
  67. :random
  68. end
  69. 1 TEARDOWN_METHODS = %w[ before_teardown teardown after_teardown ] # :nodoc:
  70. ##
  71. # Runs a single test with setup/teardown hooks.
  72. 1 def run
  73. with_info_handler do
  74. time_it do
  75. capture_exceptions do
  76. before_setup; setup; after_setup
  77. self.send self.name
  78. end
  79. TEARDOWN_METHODS.each do |hook|
  80. capture_exceptions do
  81. self.send hook
  82. end
  83. end
  84. end
  85. end
  86. Result.from self # per contract
  87. end
  88. ##
  89. # Provides before/after hooks for setup and teardown. These are
  90. # meant for library writers, NOT for regular test authors. See
  91. # #before_setup for an example.
  92. 1 module LifecycleHooks
  93. ##
  94. # Runs before every test, before setup. This hook is meant for
  95. # libraries to extend minitest. It is not meant to be used by
  96. # test developers.
  97. #
  98. # As a simplistic example:
  99. #
  100. # module MyMinitestPlugin
  101. # def before_setup
  102. # super
  103. # # ... stuff to do before setup is run
  104. # end
  105. #
  106. # def after_setup
  107. # # ... stuff to do after setup is run
  108. # super
  109. # end
  110. #
  111. # def before_teardown
  112. # super
  113. # # ... stuff to do before teardown is run
  114. # end
  115. #
  116. # def after_teardown
  117. # # ... stuff to do after teardown is run
  118. # super
  119. # end
  120. # end
  121. #
  122. # class MiniTest::Test
  123. # include MyMinitestPlugin
  124. # end
  125. 1 def before_setup; end
  126. ##
  127. # Runs before every test. Use this to set up before each test
  128. # run.
  129. 1 def setup; end
  130. ##
  131. # Runs before every test, after setup. This hook is meant for
  132. # libraries to extend minitest. It is not meant to be used by
  133. # test developers.
  134. #
  135. # See #before_setup for an example.
  136. 1 def after_setup; end
  137. ##
  138. # Runs after every test, before teardown. This hook is meant for
  139. # libraries to extend minitest. It is not meant to be used by
  140. # test developers.
  141. #
  142. # See #before_setup for an example.
  143. 1 def before_teardown; end
  144. ##
  145. # Runs after every test. Use this to clean up after each test
  146. # run.
  147. 1 def teardown; end
  148. ##
  149. # Runs after every test, after teardown. This hook is meant for
  150. # libraries to extend minitest. It is not meant to be used by
  151. # test developers.
  152. #
  153. # See #before_setup for an example.
  154. 1 def after_teardown; end
  155. end # LifecycleHooks
  156. 1 def capture_exceptions # :nodoc:
  157. yield
  158. rescue *PASSTHROUGH_EXCEPTIONS
  159. raise
  160. rescue Assertion => e
  161. self.failures << e
  162. rescue Exception => e
  163. self.failures << UnexpectedError.new(e)
  164. end
  165. 1 def with_info_handler &block # :nodoc:
  166. t0 = Minitest.clock_time
  167. handler = lambda do
  168. warn "\nCurrent: %s#%s %.2fs" % [self.class, self.name, Minitest.clock_time - t0]
  169. end
  170. self.class.on_signal ::Minitest.info_signal, handler, &block
  171. end
  172. 1 include LifecycleHooks
  173. 1 include Guard
  174. 1 extend Guard
  175. end # Test
  176. end
  177. 1 require "minitest/unit" unless defined?(MiniTest) # compatibility layer only

vender/bundle/ruby/2.6.0/gems/minitest-5.14.4/lib/minitest/unit.rb

36.0% lines covered

25 relevant lines. 9 lines covered and 16 lines missed.
    
  1. # :stopdoc:
  2. 1 unless defined?(Minitest) then
  3. # all of this crap is just to avoid circular requires and is only
  4. # needed if a user requires "minitest/unit" directly instead of
  5. # "minitest/autorun", so we also warn
  6. from = caller.reject { |s| s =~ /rubygems/ }.join("\n ")
  7. warn "Warning: you should require 'minitest/autorun' instead."
  8. warn %(Warning: or add 'gem "minitest"' before 'require "minitest/autorun"')
  9. warn "From:\n #{from}"
  10. module Minitest; end
  11. MiniTest = Minitest # prevents minitest.rb from requiring back to us
  12. require "minitest"
  13. end
  14. 1 MiniTest = Minitest unless defined?(MiniTest)
  15. 1 module Minitest
  16. 1 class Unit
  17. 1 VERSION = Minitest::VERSION
  18. 1 class TestCase < Minitest::Test
  19. 1 def self.inherited klass # :nodoc:
  20. from = caller.first
  21. warn "MiniTest::Unit::TestCase is now Minitest::Test. From #{from}"
  22. super
  23. end
  24. end
  25. 1 def self.autorun # :nodoc:
  26. from = caller.first
  27. warn "MiniTest::Unit.autorun is now Minitest.autorun. From #{from}"
  28. Minitest.autorun
  29. end
  30. 1 def self.after_tests &b # :nodoc:
  31. from = caller.first
  32. warn "MiniTest::Unit.after_tests is now Minitest.after_run. From #{from}"
  33. Minitest.after_run(&b)
  34. end
  35. end
  36. end
  37. # :startdoc:

vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/chunked.rb

41.46% lines covered

41 relevant lines. 17 lines covered and 24 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Rack
  3. # Middleware that applies chunked transfer encoding to response bodies
  4. # when the response does not include a Content-Length header.
  5. #
  6. # This supports the Trailer response header to allow the use of trailing
  7. # headers in the chunked encoding. However, using this requires you manually
  8. # specify a response body that supports a +trailers+ method. Example:
  9. #
  10. # [200, { 'Trailer' => 'Expires'}, ["Hello", "World"]]
  11. # # error raised
  12. #
  13. # body = ["Hello", "World"]
  14. # def body.trailers
  15. # { 'Expires' => Time.now.to_s }
  16. # end
  17. # [200, { 'Trailer' => 'Expires'}, body]
  18. # # No exception raised
  19. 1 class Chunked
  20. 1 include Rack::Utils
  21. # A body wrapper that emits chunked responses.
  22. 1 class Body
  23. 1 TERM = "\r\n"
  24. 1 TAIL = "0#{TERM}"
  25. # Store the response body to be chunked.
  26. 1 def initialize(body)
  27. @body = body
  28. end
  29. # For each element yielded by the response body, yield
  30. # the element in chunked encoding.
  31. 1 def each(&block)
  32. term = TERM
  33. @body.each do |chunk|
  34. size = chunk.bytesize
  35. next if size == 0
  36. yield [size.to_s(16), term, chunk.b, term].join
  37. end
  38. yield TAIL
  39. yield_trailers(&block)
  40. yield term
  41. end
  42. # Close the response body if the response body supports it.
  43. 1 def close
  44. @body.close if @body.respond_to?(:close)
  45. end
  46. 1 private
  47. # Do nothing as this class does not support trailer headers.
  48. 1 def yield_trailers
  49. end
  50. end
  51. # A body wrapper that emits chunked responses and also supports
  52. # sending Trailer headers. Note that the response body provided to
  53. # initialize must have a +trailers+ method that returns a hash
  54. # of trailer headers, and the rack response itself should have a
  55. # Trailer header listing the headers that the +trailers+ method
  56. # will return.
  57. 1 class TrailerBody < Body
  58. 1 private
  59. # Yield strings for each trailer header.
  60. 1 def yield_trailers
  61. @body.trailers.each_pair do |k, v|
  62. yield "#{k}: #{v}\r\n"
  63. end
  64. end
  65. end
  66. 1 def initialize(app)
  67. @app = app
  68. end
  69. # Whether the HTTP version supports chunked encoding (HTTP 1.1 does).
  70. 1 def chunkable_version?(ver)
  71. case ver
  72. # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
  73. # a version (nor response headers)
  74. when 'HTTP/1.0', nil, 'HTTP/0.9'
  75. false
  76. else
  77. true
  78. end
  79. end
  80. # If the rack app returns a response that should have a body,
  81. # but does not have Content-Length or Transfer-Encoding headers,
  82. # modify the response to use chunked Transfer-Encoding.
  83. 1 def call(env)
  84. status, headers, body = @app.call(env)
  85. headers = HeaderHash[headers]
  86. if chunkable_version?(env[SERVER_PROTOCOL]) &&
  87. !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
  88. !headers[CONTENT_LENGTH] &&
  89. !headers[TRANSFER_ENCODING]
  90. headers[TRANSFER_ENCODING] = 'chunked'
  91. if headers['Trailer']
  92. body = TrailerBody.new(body)
  93. else
  94. body = Body.new(body)
  95. end
  96. end
  97. [status, headers, body]
  98. end
  99. end
  100. end

vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/mime.rb

72.73% lines covered

11 relevant lines. 8 lines covered and 3 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Rack
  3. 1 module Mime
  4. # Returns String with mime type if found, otherwise use +fallback+.
  5. # +ext+ should be filename extension in the '.ext' format that
  6. # File.extname(file) returns.
  7. # +fallback+ may be any object
  8. #
  9. # Also see the documentation for MIME_TYPES
  10. #
  11. # Usage:
  12. # Rack::Mime.mime_type('.foo')
  13. #
  14. # This is a shortcut for:
  15. # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
  16. 1 def mime_type(ext, fallback = 'application/octet-stream')
  17. 4 MIME_TYPES.fetch(ext.to_s.downcase, fallback)
  18. end
  19. 1 module_function :mime_type
  20. # Returns true if the given value is a mime match for the given mime match
  21. # specification, false otherwise.
  22. #
  23. # Rack::Mime.match?('text/html', 'text/*') => true
  24. # Rack::Mime.match?('text/plain', '*') => true
  25. # Rack::Mime.match?('text/html', 'application/json') => false
  26. 1 def match?(value, matcher)
  27. v1, v2 = value.split('/', 2)
  28. m1, m2 = matcher.split('/', 2)
  29. (m1 == '*' || v1 == m1) && (m2.nil? || m2 == '*' || m2 == v2)
  30. end
  31. 1 module_function :match?
  32. # List of most common mime-types, selected various sources
  33. # according to their usefulness in a webserving scope for Ruby
  34. # users.
  35. #
  36. # To amend this list with your local mime.types list you can use:
  37. #
  38. # require 'webrick/httputils'
  39. # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types')
  40. # Rack::Mime::MIME_TYPES.merge!(list)
  41. #
  42. # N.B. On Ubuntu the mime.types file does not include the leading period, so
  43. # users may need to modify the data before merging into the hash.
  44. MIME_TYPES = {
  45. 1 ".123" => "application/vnd.lotus-1-2-3",
  46. ".3dml" => "text/vnd.in3d.3dml",
  47. ".3g2" => "video/3gpp2",
  48. ".3gp" => "video/3gpp",
  49. ".a" => "application/octet-stream",
  50. ".acc" => "application/vnd.americandynamics.acc",
  51. ".ace" => "application/x-ace-compressed",
  52. ".acu" => "application/vnd.acucobol",
  53. ".aep" => "application/vnd.audiograph",
  54. ".afp" => "application/vnd.ibm.modcap",
  55. ".ai" => "application/postscript",
  56. ".aif" => "audio/x-aiff",
  57. ".aiff" => "audio/x-aiff",
  58. ".ami" => "application/vnd.amiga.ami",
  59. ".appcache" => "text/cache-manifest",
  60. ".apr" => "application/vnd.lotus-approach",
  61. ".asc" => "application/pgp-signature",
  62. ".asf" => "video/x-ms-asf",
  63. ".asm" => "text/x-asm",
  64. ".aso" => "application/vnd.accpac.simply.aso",
  65. ".asx" => "video/x-ms-asf",
  66. ".atc" => "application/vnd.acucorp",
  67. ".atom" => "application/atom+xml",
  68. ".atomcat" => "application/atomcat+xml",
  69. ".atomsvc" => "application/atomsvc+xml",
  70. ".atx" => "application/vnd.antix.game-component",
  71. ".au" => "audio/basic",
  72. ".avi" => "video/x-msvideo",
  73. ".bat" => "application/x-msdownload",
  74. ".bcpio" => "application/x-bcpio",
  75. ".bdm" => "application/vnd.syncml.dm+wbxml",
  76. ".bh2" => "application/vnd.fujitsu.oasysprs",
  77. ".bin" => "application/octet-stream",
  78. ".bmi" => "application/vnd.bmi",
  79. ".bmp" => "image/bmp",
  80. ".box" => "application/vnd.previewsystems.box",
  81. ".btif" => "image/prs.btif",
  82. ".bz" => "application/x-bzip",
  83. ".bz2" => "application/x-bzip2",
  84. ".c" => "text/x-c",
  85. ".c4g" => "application/vnd.clonk.c4group",
  86. ".cab" => "application/vnd.ms-cab-compressed",
  87. ".cc" => "text/x-c",
  88. ".ccxml" => "application/ccxml+xml",
  89. ".cdbcmsg" => "application/vnd.contact.cmsg",
  90. ".cdkey" => "application/vnd.mediastation.cdkey",
  91. ".cdx" => "chemical/x-cdx",
  92. ".cdxml" => "application/vnd.chemdraw+xml",
  93. ".cdy" => "application/vnd.cinderella",
  94. ".cer" => "application/pkix-cert",
  95. ".cgm" => "image/cgm",
  96. ".chat" => "application/x-chat",
  97. ".chm" => "application/vnd.ms-htmlhelp",
  98. ".chrt" => "application/vnd.kde.kchart",
  99. ".cif" => "chemical/x-cif",
  100. ".cii" => "application/vnd.anser-web-certificate-issue-initiation",
  101. ".cil" => "application/vnd.ms-artgalry",
  102. ".cla" => "application/vnd.claymore",
  103. ".class" => "application/octet-stream",
  104. ".clkk" => "application/vnd.crick.clicker.keyboard",
  105. ".clkp" => "application/vnd.crick.clicker.palette",
  106. ".clkt" => "application/vnd.crick.clicker.template",
  107. ".clkw" => "application/vnd.crick.clicker.wordbank",
  108. ".clkx" => "application/vnd.crick.clicker",
  109. ".clp" => "application/x-msclip",
  110. ".cmc" => "application/vnd.cosmocaller",
  111. ".cmdf" => "chemical/x-cmdf",
  112. ".cml" => "chemical/x-cml",
  113. ".cmp" => "application/vnd.yellowriver-custom-menu",
  114. ".cmx" => "image/x-cmx",
  115. ".com" => "application/x-msdownload",
  116. ".conf" => "text/plain",
  117. ".cpio" => "application/x-cpio",
  118. ".cpp" => "text/x-c",
  119. ".cpt" => "application/mac-compactpro",
  120. ".crd" => "application/x-mscardfile",
  121. ".crl" => "application/pkix-crl",
  122. ".crt" => "application/x-x509-ca-cert",
  123. ".csh" => "application/x-csh",
  124. ".csml" => "chemical/x-csml",
  125. ".csp" => "application/vnd.commonspace",
  126. ".css" => "text/css",
  127. ".csv" => "text/csv",
  128. ".curl" => "application/vnd.curl",
  129. ".cww" => "application/prs.cww",
  130. ".cxx" => "text/x-c",
  131. ".daf" => "application/vnd.mobius.daf",
  132. ".davmount" => "application/davmount+xml",
  133. ".dcr" => "application/x-director",
  134. ".dd2" => "application/vnd.oma.dd2+xml",
  135. ".ddd" => "application/vnd.fujixerox.ddd",
  136. ".deb" => "application/x-debian-package",
  137. ".der" => "application/x-x509-ca-cert",
  138. ".dfac" => "application/vnd.dreamfactory",
  139. ".diff" => "text/x-diff",
  140. ".dis" => "application/vnd.mobius.dis",
  141. ".djv" => "image/vnd.djvu",
  142. ".djvu" => "image/vnd.djvu",
  143. ".dll" => "application/x-msdownload",
  144. ".dmg" => "application/octet-stream",
  145. ".dna" => "application/vnd.dna",
  146. ".doc" => "application/msword",
  147. ".docm" => "application/vnd.ms-word.document.macroEnabled.12",
  148. ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  149. ".dot" => "application/msword",
  150. ".dotm" => "application/vnd.ms-word.template.macroEnabled.12",
  151. ".dotx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
  152. ".dp" => "application/vnd.osgi.dp",
  153. ".dpg" => "application/vnd.dpgraph",
  154. ".dsc" => "text/prs.lines.tag",
  155. ".dtd" => "application/xml-dtd",
  156. ".dts" => "audio/vnd.dts",
  157. ".dtshd" => "audio/vnd.dts.hd",
  158. ".dv" => "video/x-dv",
  159. ".dvi" => "application/x-dvi",
  160. ".dwf" => "model/vnd.dwf",
  161. ".dwg" => "image/vnd.dwg",
  162. ".dxf" => "image/vnd.dxf",
  163. ".dxp" => "application/vnd.spotfire.dxp",
  164. ".ear" => "application/java-archive",
  165. ".ecelp4800" => "audio/vnd.nuera.ecelp4800",
  166. ".ecelp7470" => "audio/vnd.nuera.ecelp7470",
  167. ".ecelp9600" => "audio/vnd.nuera.ecelp9600",
  168. ".ecma" => "application/ecmascript",
  169. ".edm" => "application/vnd.novadigm.edm",
  170. ".edx" => "application/vnd.novadigm.edx",
  171. ".efif" => "application/vnd.picsel",
  172. ".ei6" => "application/vnd.pg.osasli",
  173. ".eml" => "message/rfc822",
  174. ".eol" => "audio/vnd.digital-winds",
  175. ".eot" => "application/vnd.ms-fontobject",
  176. ".eps" => "application/postscript",
  177. ".es3" => "application/vnd.eszigno3+xml",
  178. ".esf" => "application/vnd.epson.esf",
  179. ".etx" => "text/x-setext",
  180. ".exe" => "application/x-msdownload",
  181. ".ext" => "application/vnd.novadigm.ext",
  182. ".ez" => "application/andrew-inset",
  183. ".ez2" => "application/vnd.ezpix-album",
  184. ".ez3" => "application/vnd.ezpix-package",
  185. ".f" => "text/x-fortran",
  186. ".f77" => "text/x-fortran",
  187. ".f90" => "text/x-fortran",
  188. ".fbs" => "image/vnd.fastbidsheet",
  189. ".fdf" => "application/vnd.fdf",
  190. ".fe_launch" => "application/vnd.denovo.fcselayout-link",
  191. ".fg5" => "application/vnd.fujitsu.oasysgp",
  192. ".fli" => "video/x-fli",
  193. ".flo" => "application/vnd.micrografx.flo",
  194. ".flv" => "video/x-flv",
  195. ".flw" => "application/vnd.kde.kivio",
  196. ".flx" => "text/vnd.fmi.flexstor",
  197. ".fly" => "text/vnd.fly",
  198. ".fm" => "application/vnd.framemaker",
  199. ".fnc" => "application/vnd.frogans.fnc",
  200. ".for" => "text/x-fortran",
  201. ".fpx" => "image/vnd.fpx",
  202. ".fsc" => "application/vnd.fsc.weblaunch",
  203. ".fst" => "image/vnd.fst",
  204. ".ftc" => "application/vnd.fluxtime.clip",
  205. ".fti" => "application/vnd.anser-web-funds-transfer-initiation",
  206. ".fvt" => "video/vnd.fvt",
  207. ".fzs" => "application/vnd.fuzzysheet",
  208. ".g3" => "image/g3fax",
  209. ".gac" => "application/vnd.groove-account",
  210. ".gdl" => "model/vnd.gdl",
  211. ".gem" => "application/octet-stream",
  212. ".gemspec" => "text/x-script.ruby",
  213. ".ghf" => "application/vnd.groove-help",
  214. ".gif" => "image/gif",
  215. ".gim" => "application/vnd.groove-identity-message",
  216. ".gmx" => "application/vnd.gmx",
  217. ".gph" => "application/vnd.flographit",
  218. ".gqf" => "application/vnd.grafeq",
  219. ".gram" => "application/srgs",
  220. ".grv" => "application/vnd.groove-injector",
  221. ".grxml" => "application/srgs+xml",
  222. ".gtar" => "application/x-gtar",
  223. ".gtm" => "application/vnd.groove-tool-message",
  224. ".gtw" => "model/vnd.gtw",
  225. ".gv" => "text/vnd.graphviz",
  226. ".gz" => "application/x-gzip",
  227. ".h" => "text/x-c",
  228. ".h261" => "video/h261",
  229. ".h263" => "video/h263",
  230. ".h264" => "video/h264",
  231. ".hbci" => "application/vnd.hbci",
  232. ".hdf" => "application/x-hdf",
  233. ".hh" => "text/x-c",
  234. ".hlp" => "application/winhlp",
  235. ".hpgl" => "application/vnd.hp-hpgl",
  236. ".hpid" => "application/vnd.hp-hpid",
  237. ".hps" => "application/vnd.hp-hps",
  238. ".hqx" => "application/mac-binhex40",
  239. ".htc" => "text/x-component",
  240. ".htke" => "application/vnd.kenameaapp",
  241. ".htm" => "text/html",
  242. ".html" => "text/html",
  243. ".hvd" => "application/vnd.yamaha.hv-dic",
  244. ".hvp" => "application/vnd.yamaha.hv-voice",
  245. ".hvs" => "application/vnd.yamaha.hv-script",
  246. ".icc" => "application/vnd.iccprofile",
  247. ".ice" => "x-conference/x-cooltalk",
  248. ".ico" => "image/vnd.microsoft.icon",
  249. ".ics" => "text/calendar",
  250. ".ief" => "image/ief",
  251. ".ifb" => "text/calendar",
  252. ".ifm" => "application/vnd.shana.informed.formdata",
  253. ".igl" => "application/vnd.igloader",
  254. ".igs" => "model/iges",
  255. ".igx" => "application/vnd.micrografx.igx",
  256. ".iif" => "application/vnd.shana.informed.interchange",
  257. ".imp" => "application/vnd.accpac.simply.imp",
  258. ".ims" => "application/vnd.ms-ims",
  259. ".ipk" => "application/vnd.shana.informed.package",
  260. ".irm" => "application/vnd.ibm.rights-management",
  261. ".irp" => "application/vnd.irepository.package+xml",
  262. ".iso" => "application/octet-stream",
  263. ".itp" => "application/vnd.shana.informed.formtemplate",
  264. ".ivp" => "application/vnd.immervision-ivp",
  265. ".ivu" => "application/vnd.immervision-ivu",
  266. ".jad" => "text/vnd.sun.j2me.app-descriptor",
  267. ".jam" => "application/vnd.jam",
  268. ".jar" => "application/java-archive",
  269. ".java" => "text/x-java-source",
  270. ".jisp" => "application/vnd.jisp",
  271. ".jlt" => "application/vnd.hp-jlyt",
  272. ".jnlp" => "application/x-java-jnlp-file",
  273. ".joda" => "application/vnd.joost.joda-archive",
  274. ".jp2" => "image/jp2",
  275. ".jpeg" => "image/jpeg",
  276. ".jpg" => "image/jpeg",
  277. ".jpgv" => "video/jpeg",
  278. ".jpm" => "video/jpm",
  279. ".js" => "application/javascript",
  280. ".json" => "application/json",
  281. ".karbon" => "application/vnd.kde.karbon",
  282. ".kfo" => "application/vnd.kde.kformula",
  283. ".kia" => "application/vnd.kidspiration",
  284. ".kml" => "application/vnd.google-earth.kml+xml",
  285. ".kmz" => "application/vnd.google-earth.kmz",
  286. ".kne" => "application/vnd.kinar",
  287. ".kon" => "application/vnd.kde.kontour",
  288. ".kpr" => "application/vnd.kde.kpresenter",
  289. ".ksp" => "application/vnd.kde.kspread",
  290. ".ktz" => "application/vnd.kahootz",
  291. ".kwd" => "application/vnd.kde.kword",
  292. ".latex" => "application/x-latex",
  293. ".lbd" => "application/vnd.llamagraphics.life-balance.desktop",
  294. ".lbe" => "application/vnd.llamagraphics.life-balance.exchange+xml",
  295. ".les" => "application/vnd.hhe.lesson-player",
  296. ".link66" => "application/vnd.route66.link66+xml",
  297. ".log" => "text/plain",
  298. ".lostxml" => "application/lost+xml",
  299. ".lrm" => "application/vnd.ms-lrm",
  300. ".ltf" => "application/vnd.frogans.ltf",
  301. ".lvp" => "audio/vnd.lucent.voice",
  302. ".lwp" => "application/vnd.lotus-wordpro",
  303. ".m3u" => "audio/x-mpegurl",
  304. ".m3u8" => "application/x-mpegurl",
  305. ".m4a" => "audio/mp4a-latm",
  306. ".m4v" => "video/mp4",
  307. ".ma" => "application/mathematica",
  308. ".mag" => "application/vnd.ecowin.chart",
  309. ".man" => "text/troff",
  310. ".manifest" => "text/cache-manifest",
  311. ".mathml" => "application/mathml+xml",
  312. ".mbk" => "application/vnd.mobius.mbk",
  313. ".mbox" => "application/mbox",
  314. ".mc1" => "application/vnd.medcalcdata",
  315. ".mcd" => "application/vnd.mcd",
  316. ".mdb" => "application/x-msaccess",
  317. ".mdi" => "image/vnd.ms-modi",
  318. ".mdoc" => "text/troff",
  319. ".me" => "text/troff",
  320. ".mfm" => "application/vnd.mfmp",
  321. ".mgz" => "application/vnd.proteus.magazine",
  322. ".mid" => "audio/midi",
  323. ".midi" => "audio/midi",
  324. ".mif" => "application/vnd.mif",
  325. ".mime" => "message/rfc822",
  326. ".mj2" => "video/mj2",
  327. ".mlp" => "application/vnd.dolby.mlp",
  328. ".mmd" => "application/vnd.chipnuts.karaoke-mmd",
  329. ".mmf" => "application/vnd.smaf",
  330. ".mml" => "application/mathml+xml",
  331. ".mmr" => "image/vnd.fujixerox.edmics-mmr",
  332. ".mng" => "video/x-mng",
  333. ".mny" => "application/x-msmoney",
  334. ".mov" => "video/quicktime",
  335. ".movie" => "video/x-sgi-movie",
  336. ".mp3" => "audio/mpeg",
  337. ".mp4" => "video/mp4",
  338. ".mp4a" => "audio/mp4",
  339. ".mp4s" => "application/mp4",
  340. ".mp4v" => "video/mp4",
  341. ".mpc" => "application/vnd.mophun.certificate",
  342. ".mpd" => "application/dash+xml",
  343. ".mpeg" => "video/mpeg",
  344. ".mpg" => "video/mpeg",
  345. ".mpga" => "audio/mpeg",
  346. ".mpkg" => "application/vnd.apple.installer+xml",
  347. ".mpm" => "application/vnd.blueice.multipass",
  348. ".mpn" => "application/vnd.mophun.application",
  349. ".mpp" => "application/vnd.ms-project",
  350. ".mpy" => "application/vnd.ibm.minipay",
  351. ".mqy" => "application/vnd.mobius.mqy",
  352. ".mrc" => "application/marc",
  353. ".ms" => "text/troff",
  354. ".mscml" => "application/mediaservercontrol+xml",
  355. ".mseq" => "application/vnd.mseq",
  356. ".msf" => "application/vnd.epson.msf",
  357. ".msh" => "model/mesh",
  358. ".msi" => "application/x-msdownload",
  359. ".msl" => "application/vnd.mobius.msl",
  360. ".msty" => "application/vnd.muvee.style",
  361. ".mts" => "model/vnd.mts",
  362. ".mus" => "application/vnd.musician",
  363. ".mvb" => "application/x-msmediaview",
  364. ".mwf" => "application/vnd.mfer",
  365. ".mxf" => "application/mxf",
  366. ".mxl" => "application/vnd.recordare.musicxml",
  367. ".mxml" => "application/xv+xml",
  368. ".mxs" => "application/vnd.triscape.mxs",
  369. ".mxu" => "video/vnd.mpegurl",
  370. ".n" => "application/vnd.nokia.n-gage.symbian.install",
  371. ".nc" => "application/x-netcdf",
  372. ".ngdat" => "application/vnd.nokia.n-gage.data",
  373. ".nlu" => "application/vnd.neurolanguage.nlu",
  374. ".nml" => "application/vnd.enliven",
  375. ".nnd" => "application/vnd.noblenet-directory",
  376. ".nns" => "application/vnd.noblenet-sealer",
  377. ".nnw" => "application/vnd.noblenet-web",
  378. ".npx" => "image/vnd.net-fpx",
  379. ".nsf" => "application/vnd.lotus-notes",
  380. ".oa2" => "application/vnd.fujitsu.oasys2",
  381. ".oa3" => "application/vnd.fujitsu.oasys3",
  382. ".oas" => "application/vnd.fujitsu.oasys",
  383. ".obd" => "application/x-msbinder",
  384. ".oda" => "application/oda",
  385. ".odc" => "application/vnd.oasis.opendocument.chart",
  386. ".odf" => "application/vnd.oasis.opendocument.formula",
  387. ".odg" => "application/vnd.oasis.opendocument.graphics",
  388. ".odi" => "application/vnd.oasis.opendocument.image",
  389. ".odp" => "application/vnd.oasis.opendocument.presentation",
  390. ".ods" => "application/vnd.oasis.opendocument.spreadsheet",
  391. ".odt" => "application/vnd.oasis.opendocument.text",
  392. ".oga" => "audio/ogg",
  393. ".ogg" => "application/ogg",
  394. ".ogv" => "video/ogg",
  395. ".ogx" => "application/ogg",
  396. ".org" => "application/vnd.lotus-organizer",
  397. ".otc" => "application/vnd.oasis.opendocument.chart-template",
  398. ".otf" => "application/vnd.oasis.opendocument.formula-template",
  399. ".otg" => "application/vnd.oasis.opendocument.graphics-template",
  400. ".oth" => "application/vnd.oasis.opendocument.text-web",
  401. ".oti" => "application/vnd.oasis.opendocument.image-template",
  402. ".otm" => "application/vnd.oasis.opendocument.text-master",
  403. ".ots" => "application/vnd.oasis.opendocument.spreadsheet-template",
  404. ".ott" => "application/vnd.oasis.opendocument.text-template",
  405. ".oxt" => "application/vnd.openofficeorg.extension",
  406. ".p" => "text/x-pascal",
  407. ".p10" => "application/pkcs10",
  408. ".p12" => "application/x-pkcs12",
  409. ".p7b" => "application/x-pkcs7-certificates",
  410. ".p7m" => "application/pkcs7-mime",
  411. ".p7r" => "application/x-pkcs7-certreqresp",
  412. ".p7s" => "application/pkcs7-signature",
  413. ".pas" => "text/x-pascal",
  414. ".pbd" => "application/vnd.powerbuilder6",
  415. ".pbm" => "image/x-portable-bitmap",
  416. ".pcl" => "application/vnd.hp-pcl",
  417. ".pclxl" => "application/vnd.hp-pclxl",
  418. ".pcx" => "image/x-pcx",
  419. ".pdb" => "chemical/x-pdb",
  420. ".pdf" => "application/pdf",
  421. ".pem" => "application/x-x509-ca-cert",
  422. ".pfr" => "application/font-tdpfr",
  423. ".pgm" => "image/x-portable-graymap",
  424. ".pgn" => "application/x-chess-pgn",
  425. ".pgp" => "application/pgp-encrypted",
  426. ".pic" => "image/x-pict",
  427. ".pict" => "image/pict",
  428. ".pkg" => "application/octet-stream",
  429. ".pki" => "application/pkixcmp",
  430. ".pkipath" => "application/pkix-pkipath",
  431. ".pl" => "text/x-script.perl",
  432. ".plb" => "application/vnd.3gpp.pic-bw-large",
  433. ".plc" => "application/vnd.mobius.plc",
  434. ".plf" => "application/vnd.pocketlearn",
  435. ".pls" => "application/pls+xml",
  436. ".pm" => "text/x-script.perl-module",
  437. ".pml" => "application/vnd.ctc-posml",
  438. ".png" => "image/png",
  439. ".pnm" => "image/x-portable-anymap",
  440. ".pntg" => "image/x-macpaint",
  441. ".portpkg" => "application/vnd.macports.portpkg",
  442. ".pot" => "application/vnd.ms-powerpoint",
  443. ".potm" => "application/vnd.ms-powerpoint.template.macroEnabled.12",
  444. ".potx" => "application/vnd.openxmlformats-officedocument.presentationml.template",
  445. ".ppa" => "application/vnd.ms-powerpoint",
  446. ".ppam" => "application/vnd.ms-powerpoint.addin.macroEnabled.12",
  447. ".ppd" => "application/vnd.cups-ppd",
  448. ".ppm" => "image/x-portable-pixmap",
  449. ".pps" => "application/vnd.ms-powerpoint",
  450. ".ppsm" => "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
  451. ".ppsx" => "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
  452. ".ppt" => "application/vnd.ms-powerpoint",
  453. ".pptm" => "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
  454. ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
  455. ".prc" => "application/vnd.palm",
  456. ".pre" => "application/vnd.lotus-freelance",
  457. ".prf" => "application/pics-rules",
  458. ".ps" => "application/postscript",
  459. ".psb" => "application/vnd.3gpp.pic-bw-small",
  460. ".psd" => "image/vnd.adobe.photoshop",
  461. ".ptid" => "application/vnd.pvi.ptid1",
  462. ".pub" => "application/x-mspublisher",
  463. ".pvb" => "application/vnd.3gpp.pic-bw-var",
  464. ".pwn" => "application/vnd.3m.post-it-notes",
  465. ".py" => "text/x-script.python",
  466. ".pya" => "audio/vnd.ms-playready.media.pya",
  467. ".pyv" => "video/vnd.ms-playready.media.pyv",
  468. ".qam" => "application/vnd.epson.quickanime",
  469. ".qbo" => "application/vnd.intu.qbo",
  470. ".qfx" => "application/vnd.intu.qfx",
  471. ".qps" => "application/vnd.publishare-delta-tree",
  472. ".qt" => "video/quicktime",
  473. ".qtif" => "image/x-quicktime",
  474. ".qxd" => "application/vnd.quark.quarkxpress",
  475. ".ra" => "audio/x-pn-realaudio",
  476. ".rake" => "text/x-script.ruby",
  477. ".ram" => "audio/x-pn-realaudio",
  478. ".rar" => "application/x-rar-compressed",
  479. ".ras" => "image/x-cmu-raster",
  480. ".rb" => "text/x-script.ruby",
  481. ".rcprofile" => "application/vnd.ipunplugged.rcprofile",
  482. ".rdf" => "application/rdf+xml",
  483. ".rdz" => "application/vnd.data-vision.rdz",
  484. ".rep" => "application/vnd.businessobjects",
  485. ".rgb" => "image/x-rgb",
  486. ".rif" => "application/reginfo+xml",
  487. ".rl" => "application/resource-lists+xml",
  488. ".rlc" => "image/vnd.fujixerox.edmics-rlc",
  489. ".rld" => "application/resource-lists-diff+xml",
  490. ".rm" => "application/vnd.rn-realmedia",
  491. ".rmp" => "audio/x-pn-realaudio-plugin",
  492. ".rms" => "application/vnd.jcp.javame.midlet-rms",
  493. ".rnc" => "application/relax-ng-compact-syntax",
  494. ".roff" => "text/troff",
  495. ".rpm" => "application/x-redhat-package-manager",
  496. ".rpss" => "application/vnd.nokia.radio-presets",
  497. ".rpst" => "application/vnd.nokia.radio-preset",
  498. ".rq" => "application/sparql-query",
  499. ".rs" => "application/rls-services+xml",
  500. ".rsd" => "application/rsd+xml",
  501. ".rss" => "application/rss+xml",
  502. ".rtf" => "application/rtf",
  503. ".rtx" => "text/richtext",
  504. ".ru" => "text/x-script.ruby",
  505. ".s" => "text/x-asm",
  506. ".saf" => "application/vnd.yamaha.smaf-audio",
  507. ".sbml" => "application/sbml+xml",
  508. ".sc" => "application/vnd.ibm.secure-container",
  509. ".scd" => "application/x-msschedule",
  510. ".scm" => "application/vnd.lotus-screencam",
  511. ".scq" => "application/scvp-cv-request",
  512. ".scs" => "application/scvp-cv-response",
  513. ".sdkm" => "application/vnd.solent.sdkm+xml",
  514. ".sdp" => "application/sdp",
  515. ".see" => "application/vnd.seemail",
  516. ".sema" => "application/vnd.sema",
  517. ".semd" => "application/vnd.semd",
  518. ".semf" => "application/vnd.semf",
  519. ".setpay" => "application/set-payment-initiation",
  520. ".setreg" => "application/set-registration-initiation",
  521. ".sfd" => "application/vnd.hydrostatix.sof-data",
  522. ".sfs" => "application/vnd.spotfire.sfs",
  523. ".sgm" => "text/sgml",
  524. ".sgml" => "text/sgml",
  525. ".sh" => "application/x-sh",
  526. ".shar" => "application/x-shar",
  527. ".shf" => "application/shf+xml",
  528. ".sig" => "application/pgp-signature",
  529. ".sit" => "application/x-stuffit",
  530. ".sitx" => "application/x-stuffitx",
  531. ".skp" => "application/vnd.koan",
  532. ".slt" => "application/vnd.epson.salt",
  533. ".smi" => "application/smil+xml",
  534. ".snd" => "audio/basic",
  535. ".so" => "application/octet-stream",
  536. ".spf" => "application/vnd.yamaha.smaf-phrase",
  537. ".spl" => "application/x-futuresplash",
  538. ".spot" => "text/vnd.in3d.spot",
  539. ".spp" => "application/scvp-vp-response",
  540. ".spq" => "application/scvp-vp-request",
  541. ".src" => "application/x-wais-source",
  542. ".srt" => "text/srt",
  543. ".srx" => "application/sparql-results+xml",
  544. ".sse" => "application/vnd.kodak-descriptor",
  545. ".ssf" => "application/vnd.epson.ssf",
  546. ".ssml" => "application/ssml+xml",
  547. ".stf" => "application/vnd.wt.stf",
  548. ".stk" => "application/hyperstudio",
  549. ".str" => "application/vnd.pg.format",
  550. ".sus" => "application/vnd.sus-calendar",
  551. ".sv4cpio" => "application/x-sv4cpio",
  552. ".sv4crc" => "application/x-sv4crc",
  553. ".svd" => "application/vnd.svd",
  554. ".svg" => "image/svg+xml",
  555. ".svgz" => "image/svg+xml",
  556. ".swf" => "application/x-shockwave-flash",
  557. ".swi" => "application/vnd.arastra.swi",
  558. ".t" => "text/troff",
  559. ".tao" => "application/vnd.tao.intent-module-archive",
  560. ".tar" => "application/x-tar",
  561. ".tbz" => "application/x-bzip-compressed-tar",
  562. ".tcap" => "application/vnd.3gpp2.tcap",
  563. ".tcl" => "application/x-tcl",
  564. ".tex" => "application/x-tex",
  565. ".texi" => "application/x-texinfo",
  566. ".texinfo" => "application/x-texinfo",
  567. ".text" => "text/plain",
  568. ".tif" => "image/tiff",
  569. ".tiff" => "image/tiff",
  570. ".tmo" => "application/vnd.tmobile-livetv",
  571. ".torrent" => "application/x-bittorrent",
  572. ".tpl" => "application/vnd.groove-tool-template",
  573. ".tpt" => "application/vnd.trid.tpt",
  574. ".tr" => "text/troff",
  575. ".tra" => "application/vnd.trueapp",
  576. ".trm" => "application/x-msterminal",
  577. ".ts" => "video/mp2t",
  578. ".tsv" => "text/tab-separated-values",
  579. ".ttf" => "application/octet-stream",
  580. ".twd" => "application/vnd.simtech-mindmapper",
  581. ".txd" => "application/vnd.genomatix.tuxedo",
  582. ".txf" => "application/vnd.mobius.txf",
  583. ".txt" => "text/plain",
  584. ".ufd" => "application/vnd.ufdl",
  585. ".umj" => "application/vnd.umajin",
  586. ".unityweb" => "application/vnd.unity",
  587. ".uoml" => "application/vnd.uoml+xml",
  588. ".uri" => "text/uri-list",
  589. ".ustar" => "application/x-ustar",
  590. ".utz" => "application/vnd.uiq.theme",
  591. ".uu" => "text/x-uuencode",
  592. ".vcd" => "application/x-cdlink",
  593. ".vcf" => "text/x-vcard",
  594. ".vcg" => "application/vnd.groove-vcard",
  595. ".vcs" => "text/x-vcalendar",
  596. ".vcx" => "application/vnd.vcx",
  597. ".vis" => "application/vnd.visionary",
  598. ".viv" => "video/vnd.vivo",
  599. ".vrml" => "model/vrml",
  600. ".vsd" => "application/vnd.visio",
  601. ".vsf" => "application/vnd.vsf",
  602. ".vtt" => "text/vtt",
  603. ".vtu" => "model/vnd.vtu",
  604. ".vxml" => "application/voicexml+xml",
  605. ".war" => "application/java-archive",
  606. ".wasm" => "application/wasm",
  607. ".wav" => "audio/x-wav",
  608. ".wax" => "audio/x-ms-wax",
  609. ".wbmp" => "image/vnd.wap.wbmp",
  610. ".wbs" => "application/vnd.criticaltools.wbs+xml",
  611. ".wbxml" => "application/vnd.wap.wbxml",
  612. ".webm" => "video/webm",
  613. ".wm" => "video/x-ms-wm",
  614. ".wma" => "audio/x-ms-wma",
  615. ".wmd" => "application/x-ms-wmd",
  616. ".wmf" => "application/x-msmetafile",
  617. ".wml" => "text/vnd.wap.wml",
  618. ".wmlc" => "application/vnd.wap.wmlc",
  619. ".wmls" => "text/vnd.wap.wmlscript",
  620. ".wmlsc" => "application/vnd.wap.wmlscriptc",
  621. ".wmv" => "video/x-ms-wmv",
  622. ".wmx" => "video/x-ms-wmx",
  623. ".wmz" => "application/x-ms-wmz",
  624. ".woff" => "application/font-woff",
  625. ".woff2" => "application/font-woff2",
  626. ".wpd" => "application/vnd.wordperfect",
  627. ".wpl" => "application/vnd.ms-wpl",
  628. ".wps" => "application/vnd.ms-works",
  629. ".wqd" => "application/vnd.wqd",
  630. ".wri" => "application/x-mswrite",
  631. ".wrl" => "model/vrml",
  632. ".wsdl" => "application/wsdl+xml",
  633. ".wspolicy" => "application/wspolicy+xml",
  634. ".wtb" => "application/vnd.webturbo",
  635. ".wvx" => "video/x-ms-wvx",
  636. ".x3d" => "application/vnd.hzn-3d-crossword",
  637. ".xar" => "application/vnd.xara",
  638. ".xbd" => "application/vnd.fujixerox.docuworks.binder",
  639. ".xbm" => "image/x-xbitmap",
  640. ".xdm" => "application/vnd.syncml.dm+xml",
  641. ".xdp" => "application/vnd.adobe.xdp+xml",
  642. ".xdw" => "application/vnd.fujixerox.docuworks",
  643. ".xenc" => "application/xenc+xml",
  644. ".xer" => "application/patch-ops-error+xml",
  645. ".xfdf" => "application/vnd.adobe.xfdf",
  646. ".xfdl" => "application/vnd.xfdl",
  647. ".xhtml" => "application/xhtml+xml",
  648. ".xif" => "image/vnd.xiff",
  649. ".xla" => "application/vnd.ms-excel",
  650. ".xlam" => "application/vnd.ms-excel.addin.macroEnabled.12",
  651. ".xls" => "application/vnd.ms-excel",
  652. ".xlsb" => "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
  653. ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  654. ".xlsm" => "application/vnd.ms-excel.sheet.macroEnabled.12",
  655. ".xlt" => "application/vnd.ms-excel",
  656. ".xltx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
  657. ".xml" => "application/xml",
  658. ".xo" => "application/vnd.olpc-sugar",
  659. ".xop" => "application/xop+xml",
  660. ".xpm" => "image/x-xpixmap",
  661. ".xpr" => "application/vnd.is-xpr",
  662. ".xps" => "application/vnd.ms-xpsdocument",
  663. ".xpw" => "application/vnd.intercon.formnet",
  664. ".xsl" => "application/xml",
  665. ".xslt" => "application/xslt+xml",
  666. ".xsm" => "application/vnd.syncml+xml",
  667. ".xspf" => "application/xspf+xml",
  668. ".xul" => "application/vnd.mozilla.xul+xml",
  669. ".xwd" => "image/x-xwindowdump",
  670. ".xyz" => "chemical/x-xyz",
  671. ".yaml" => "text/yaml",
  672. ".yml" => "text/yaml",
  673. ".zaz" => "application/vnd.zzazz.deck+xml",
  674. ".zip" => "application/zip",
  675. ".zmm" => "application/vnd.handheld-entertainment+xml",
  676. }
  677. end
  678. end

vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/mock.rb

60.8% lines covered

125 relevant lines. 76 lines covered and 49 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require 'uri'
  3. 1 require 'stringio'
  4. 1 require_relative '../rack'
  5. 1 require 'cgi/cookie'
  6. 1 module Rack
  7. # Rack::MockRequest helps testing your Rack application without
  8. # actually using HTTP.
  9. #
  10. # After performing a request on a URL with get/post/put/patch/delete, it
  11. # returns a MockResponse with useful helper methods for effective
  12. # testing.
  13. #
  14. # You can pass a hash with additional configuration to the
  15. # get/post/put/patch/delete.
  16. # <tt>:input</tt>:: A String or IO-like to be used as rack.input.
  17. # <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
  18. # <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
  19. 1 class MockRequest
  20. 1 class FatalWarning < RuntimeError
  21. end
  22. 1 class FatalWarner
  23. 1 def puts(warning)
  24. raise FatalWarning, warning
  25. end
  26. 1 def write(warning)
  27. raise FatalWarning, warning
  28. end
  29. 1 def flush
  30. end
  31. 1 def string
  32. ""
  33. end
  34. end
  35. DEFAULT_ENV = {
  36. 1 RACK_VERSION => Rack::VERSION,
  37. RACK_INPUT => StringIO.new,
  38. RACK_ERRORS => StringIO.new,
  39. RACK_MULTITHREAD => true,
  40. RACK_MULTIPROCESS => true,
  41. RACK_RUNONCE => false,
  42. }.freeze
  43. 1 def initialize(app)
  44. @app = app
  45. end
  46. # Make a GET request and return a MockResponse. See #request.
  47. 1 def get(uri, opts = {}) request(GET, uri, opts) end
  48. # Make a POST request and return a MockResponse. See #request.
  49. 1 def post(uri, opts = {}) request(POST, uri, opts) end
  50. # Make a PUT request and return a MockResponse. See #request.
  51. 1 def put(uri, opts = {}) request(PUT, uri, opts) end
  52. # Make a PATCH request and return a MockResponse. See #request.
  53. 1 def patch(uri, opts = {}) request(PATCH, uri, opts) end
  54. # Make a DELETE request and return a MockResponse. See #request.
  55. 1 def delete(uri, opts = {}) request(DELETE, uri, opts) end
  56. # Make a HEAD request and return a MockResponse. See #request.
  57. 1 def head(uri, opts = {}) request(HEAD, uri, opts) end
  58. # Make an OPTIONS request and return a MockResponse. See #request.
  59. 1 def options(uri, opts = {}) request(OPTIONS, uri, opts) end
  60. # Make a request using the given request method for the given
  61. # uri to the rack application and return a MockResponse.
  62. # Options given are passed to MockRequest.env_for.
  63. 1 def request(method = GET, uri = "", opts = {})
  64. env = self.class.env_for(uri, opts.merge(method: method))
  65. if opts[:lint]
  66. app = Rack::Lint.new(@app)
  67. else
  68. app = @app
  69. end
  70. errors = env[RACK_ERRORS]
  71. status, headers, body = app.call(env)
  72. MockResponse.new(status, headers, body, errors)
  73. ensure
  74. body.close if body.respond_to?(:close)
  75. end
  76. # For historical reasons, we're pinning to RFC 2396.
  77. # URI::Parser = URI::RFC2396_Parser
  78. 1 def self.parse_uri_rfc2396(uri)
  79. 3 @parser ||= URI::Parser.new
  80. 3 @parser.parse(uri)
  81. end
  82. # Return the Rack environment used for a request to +uri+.
  83. # All options that are strings are added to the returned environment.
  84. # Options:
  85. # :fatal :: Whether to raise an exception if request outputs to rack.errors
  86. # :input :: The rack.input to set
  87. # :method :: The HTTP request method to use
  88. # :params :: The params to use
  89. # :script_name :: The SCRIPT_NAME to set
  90. 1 def self.env_for(uri = "", opts = {})
  91. 3 uri = parse_uri_rfc2396(uri)
  92. 3 uri.path = "/#{uri.path}" unless uri.path[0] == ?/
  93. 3 env = DEFAULT_ENV.dup
  94. 3 env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
  95. 3 env[SERVER_NAME] = (uri.host || "example.org").b
  96. 3 env[SERVER_PORT] = (uri.port ? uri.port.to_s : "80").b
  97. 3 env[QUERY_STRING] = (uri.query.to_s).b
  98. 3 env[PATH_INFO] = ((!uri.path || uri.path.empty?) ? "/" : uri.path).b
  99. 3 env[RACK_URL_SCHEME] = (uri.scheme || "http").b
  100. 3 env[HTTPS] = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
  101. 3 env[SCRIPT_NAME] = opts[:script_name] || ""
  102. 3 if opts[:fatal]
  103. env[RACK_ERRORS] = FatalWarner.new
  104. else
  105. 3 env[RACK_ERRORS] = StringIO.new
  106. end
  107. 3 if params = opts[:params]
  108. if env[REQUEST_METHOD] == GET
  109. params = Utils.parse_nested_query(params) if params.is_a?(String)
  110. params.update(Utils.parse_nested_query(env[QUERY_STRING]))
  111. env[QUERY_STRING] = Utils.build_nested_query(params)
  112. elsif !opts.has_key?(:input)
  113. opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
  114. if params.is_a?(Hash)
  115. if data = Rack::Multipart.build_multipart(params)
  116. opts[:input] = data
  117. opts["CONTENT_LENGTH"] ||= data.length.to_s
  118. opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
  119. else
  120. opts[:input] = Utils.build_nested_query(params)
  121. end
  122. else
  123. opts[:input] = params
  124. end
  125. end
  126. end
  127. 3 empty_str = String.new
  128. 3 opts[:input] ||= empty_str
  129. 3 if String === opts[:input]
  130. 3 rack_input = StringIO.new(opts[:input])
  131. else
  132. rack_input = opts[:input]
  133. end
  134. 3 rack_input.set_encoding(Encoding::BINARY)
  135. 3 env[RACK_INPUT] = rack_input
  136. 3 env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
  137. 3 opts.each { |field, value|
  138. 30 env[field] = value if String === field
  139. }
  140. 3 env
  141. end
  142. end
  143. # Rack::MockResponse provides useful helpers for testing your apps.
  144. # Usually, you don't create the MockResponse on your own, but use
  145. # MockRequest.
  146. 1 class MockResponse < Rack::Response
  147. 1 class << self
  148. 1 alias [] new
  149. end
  150. # Headers
  151. 1 attr_reader :original_headers, :cookies
  152. # Errors
  153. 1 attr_accessor :errors
  154. 1 def initialize(status, headers, body, errors = StringIO.new(""))
  155. 1 @original_headers = headers
  156. 1 @errors = errors.string if errors.respond_to?(:string)
  157. 1 @cookies = parse_cookies_from_header
  158. 1 super(body, status, headers)
  159. 1 buffered_body!
  160. end
  161. 1 def =~(other)
  162. body =~ other
  163. end
  164. 1 def match(other)
  165. body.match other
  166. end
  167. 1 def body
  168. # FIXME: apparently users of MockResponse expect the return value of
  169. # MockResponse#body to be a string. However, the real response object
  170. # returns the body as a list.
  171. #
  172. # See spec_showstatus.rb:
  173. #
  174. # should "not replace existing messages" do
  175. # ...
  176. # res.body.should == "foo!"
  177. # end
  178. 1 buffer = String.new
  179. 1 super.each do |chunk|
  180. 1 buffer << chunk
  181. end
  182. 1 return buffer
  183. end
  184. 1 def empty?
  185. [201, 204, 304].include? status
  186. end
  187. 1 def cookie(name)
  188. cookies.fetch(name, nil)
  189. end
  190. 1 private
  191. 1 def parse_cookies_from_header
  192. 1 cookies = Hash.new
  193. 1 if original_headers.has_key? 'Set-Cookie'
  194. set_cookie_header = original_headers.fetch('Set-Cookie')
  195. set_cookie_header.split("\n").each do |cookie|
  196. cookie_name, cookie_filling = cookie.split('=', 2)
  197. cookie_attributes = identify_cookie_attributes cookie_filling
  198. parsed_cookie = CGI::Cookie.new(
  199. 'name' => cookie_name.strip,
  200. 'value' => cookie_attributes.fetch('value'),
  201. 'path' => cookie_attributes.fetch('path', nil),
  202. 'domain' => cookie_attributes.fetch('domain', nil),
  203. 'expires' => cookie_attributes.fetch('expires', nil),
  204. 'secure' => cookie_attributes.fetch('secure', false)
  205. )
  206. cookies.store(cookie_name, parsed_cookie)
  207. end
  208. end
  209. 1 cookies
  210. end
  211. 1 def identify_cookie_attributes(cookie_filling)
  212. cookie_bits = cookie_filling.split(';')
  213. cookie_attributes = Hash.new
  214. cookie_attributes.store('value', cookie_bits[0].strip)
  215. cookie_bits.each do |bit|
  216. if bit.include? '='
  217. cookie_attribute, attribute_value = bit.split('=')
  218. cookie_attributes.store(cookie_attribute.strip, attribute_value.strip)
  219. if cookie_attribute.include? 'max-age'
  220. cookie_attributes.store('expires', Time.now + attribute_value.strip.to_i)
  221. end
  222. end
  223. if bit.include? 'secure'
  224. cookie_attributes.store('secure', true)
  225. end
  226. end
  227. cookie_attributes
  228. end
  229. end
  230. end

vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/response.rb

65.63% lines covered

160 relevant lines. 105 lines covered and 55 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require 'time'
  3. 1 module Rack
  4. # Rack::Response provides a convenient interface to create a Rack
  5. # response.
  6. #
  7. # It allows setting of headers and cookies, and provides useful
  8. # defaults (an OK response with empty headers and body).
  9. #
  10. # You can use Response#write to iteratively generate your response,
  11. # but note that this is buffered by Rack::Response until you call
  12. # +finish+. +finish+ however can take a block inside which calls to
  13. # +write+ are synchronous with the Rack response.
  14. #
  15. # Your application's +call+ should end returning Response#finish.
  16. 1 class Response
  17. 1 def self.[](status, headers, body)
  18. self.new(body, status, headers)
  19. end
  20. 1 CHUNKED = 'chunked'
  21. 1 STATUS_WITH_NO_ENTITY_BODY = Utils::STATUS_WITH_NO_ENTITY_BODY
  22. 1 attr_accessor :length, :status, :body
  23. 1 attr_reader :headers
  24. # @deprecated Use {#headers} instead.
  25. 1 alias header headers
  26. # Initialize the response object with the specified body, status
  27. # and headers.
  28. #
  29. # @param body [nil, #each, #to_str] the response body.
  30. # @param status [Integer] the integer status as defined by the
  31. # HTTP protocol RFCs.
  32. # @param headers [#each] a list of key-value header pairs which
  33. # conform to the HTTP protocol RFCs.
  34. #
  35. # Providing a body which responds to #to_str is legacy behaviour.
  36. 1 def initialize(body = nil, status = 200, headers = {})
  37. 1 @status = status.to_i
  38. 1 @headers = Utils::HeaderHash[headers]
  39. 1 @writer = self.method(:append)
  40. 1 @block = nil
  41. # Keep track of whether we have expanded the user supplied body.
  42. 1 if body.nil?
  43. @body = []
  44. @buffered = true
  45. @length = 0
  46. 1 elsif body.respond_to?(:to_str)
  47. @body = [body]
  48. @buffered = true
  49. @length = body.to_str.bytesize
  50. else
  51. 1 @body = body
  52. 1 @buffered = false
  53. 1 @length = 0
  54. end
  55. 1 yield self if block_given?
  56. end
  57. 1 def redirect(target, status = 302)
  58. self.status = status
  59. self.location = target
  60. end
  61. 1 def chunked?
  62. 1 CHUNKED == get_header(TRANSFER_ENCODING)
  63. end
  64. # Generate a response array consistent with the requirements of the SPEC.
  65. # @return [Array] a 3-tuple suitable of `[status, headers, body]`
  66. # which is suitable to be returned from the middleware `#call(env)` method.
  67. 1 def finish(&block)
  68. 1 if STATUS_WITH_NO_ENTITY_BODY[status.to_i]
  69. delete_header CONTENT_TYPE
  70. delete_header CONTENT_LENGTH
  71. close
  72. return [@status, @headers, []]
  73. else
  74. 1 if block_given?
  75. @block = block
  76. return [@status, @headers, self]
  77. else
  78. 1 return [@status, @headers, @body]
  79. end
  80. end
  81. end
  82. 1 alias to_a finish # For *response
  83. 1 def each(&callback)
  84. @body.each(&callback)
  85. @buffered = true
  86. if @block
  87. @writer = callback
  88. @block.call(self)
  89. end
  90. end
  91. # Append to body and update Content-Length.
  92. #
  93. # NOTE: Do not mix #write and direct #body access!
  94. #
  95. 1 def write(chunk)
  96. buffered_body!
  97. @writer.call(chunk.to_s)
  98. end
  99. 1 def close
  100. @body.close if @body.respond_to?(:close)
  101. end
  102. 1 def empty?
  103. @block == nil && @body.empty?
  104. end
  105. 1 def has_header?(key); headers.key? key; end
  106. 2 def get_header(key); headers[key]; end
  107. 2 def set_header(key, v); headers[key] = v; end
  108. 1 def delete_header(key); headers.delete key; end
  109. 1 alias :[] :get_header
  110. 1 alias :[]= :set_header
  111. 1 module Helpers
  112. 1 def invalid?; status < 100 || status >= 600; end
  113. 1 def informational?; status >= 100 && status < 200; end
  114. 1 def successful?; status >= 200 && status < 300; end
  115. 1 def redirection?; status >= 300 && status < 400; end
  116. 1 def client_error?; status >= 400 && status < 500; end
  117. 1 def server_error?; status >= 500 && status < 600; end
  118. 1 def ok?; status == 200; end
  119. 1 def created?; status == 201; end
  120. 1 def accepted?; status == 202; end
  121. 1 def no_content?; status == 204; end
  122. 1 def moved_permanently?; status == 301; end
  123. 1 def bad_request?; status == 400; end
  124. 1 def unauthorized?; status == 401; end
  125. 1 def forbidden?; status == 403; end
  126. 1 def not_found?; status == 404; end
  127. 1 def method_not_allowed?; status == 405; end
  128. 1 def precondition_failed?; status == 412; end
  129. 1 def unprocessable?; status == 422; end
  130. 1 def redirect?; [301, 302, 303, 307, 308].include? status; end
  131. 1 def include?(header)
  132. has_header? header
  133. end
  134. # Add a header that may have multiple values.
  135. #
  136. # Example:
  137. # response.add_header 'Vary', 'Accept-Encoding'
  138. # response.add_header 'Vary', 'Cookie'
  139. #
  140. # assert_equal 'Accept-Encoding,Cookie', response.get_header('Vary')
  141. #
  142. # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
  143. 1 def add_header(key, v)
  144. if v.nil?
  145. get_header key
  146. elsif has_header? key
  147. set_header key, "#{get_header key},#{v}"
  148. else
  149. set_header key, v
  150. end
  151. end
  152. # Get the content type of the response.
  153. 1 def content_type
  154. get_header CONTENT_TYPE
  155. end
  156. # Set the content type of the response.
  157. 1 def content_type=(content_type)
  158. set_header CONTENT_TYPE, content_type
  159. end
  160. 1 def media_type
  161. MediaType.type(content_type)
  162. end
  163. 1 def media_type_params
  164. MediaType.params(content_type)
  165. end
  166. 1 def content_length
  167. cl = get_header CONTENT_LENGTH
  168. cl ? cl.to_i : cl
  169. end
  170. 1 def location
  171. get_header "Location"
  172. end
  173. 1 def location=(location)
  174. set_header "Location", location
  175. end
  176. 1 def set_cookie(key, value)
  177. cookie_header = get_header SET_COOKIE
  178. set_header SET_COOKIE, ::Rack::Utils.add_cookie_to_header(cookie_header, key, value)
  179. end
  180. 1 def delete_cookie(key, value = {})
  181. set_header SET_COOKIE, ::Rack::Utils.add_remove_cookie_to_header(get_header(SET_COOKIE), key, value)
  182. end
  183. 1 def set_cookie_header
  184. get_header SET_COOKIE
  185. end
  186. 1 def set_cookie_header=(v)
  187. set_header SET_COOKIE, v
  188. end
  189. 1 def cache_control
  190. 4 get_header CACHE_CONTROL
  191. end
  192. 1 def cache_control=(v)
  193. set_header CACHE_CONTROL, v
  194. end
  195. # Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
  196. 1 def do_not_cache!
  197. set_header CACHE_CONTROL, "no-cache, must-revalidate"
  198. set_header EXPIRES, Time.now.httpdate
  199. end
  200. # Specify that the content should be cached.
  201. # @param duration [Integer] The number of seconds until the cache expires.
  202. # @option directive [String] The cache control directive, one of "public", "private", "no-cache" or "no-store".
  203. 1 def cache!(duration = 3600, directive: "public")
  204. unless headers[CACHE_CONTROL] =~ /no-cache/
  205. set_header CACHE_CONTROL, "#{directive}, max-age=#{duration}"
  206. set_header EXPIRES, (Time.now + duration).httpdate
  207. end
  208. end
  209. 1 def etag
  210. 1 get_header ETAG
  211. end
  212. 1 def etag=(v)
  213. set_header ETAG, v
  214. end
  215. 1 protected
  216. 1 def buffered_body!
  217. 1 return if @buffered
  218. 1 if @body.is_a?(Array)
  219. # The user supplied body was an array:
  220. @body = @body.compact
  221. @body.each do |part|
  222. @length += part.to_s.bytesize
  223. end
  224. else
  225. # Turn the user supplied body into a buffered array:
  226. 1 body = @body
  227. 1 @body = Array.new
  228. 1 body.each do |part|
  229. 1 @writer.call(part.to_s)
  230. end
  231. 1 body.close if body.respond_to?(:close)
  232. end
  233. 1 @buffered = true
  234. end
  235. 1 def append(chunk)
  236. 1 @body << chunk
  237. 1 unless chunked?
  238. 1 @length += chunk.bytesize
  239. 1 set_header(CONTENT_LENGTH, @length.to_s)
  240. end
  241. 1 return chunk
  242. end
  243. end
  244. 1 include Helpers
  245. 1 class Raw
  246. 1 include Helpers
  247. 1 attr_reader :headers
  248. 1 attr_accessor :status
  249. 1 def initialize(status, headers)
  250. @status = status
  251. @headers = headers
  252. end
  253. 1 def has_header?(key); headers.key? key; end
  254. 1 def get_header(key); headers[key]; end
  255. 1 def set_header(key, v); headers[key] = v; end
  256. 1 def delete_header(key); headers.delete key; end
  257. end
  258. end
  259. end

vender/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb

39.02% lines covered

246 relevant lines. 96 lines covered and 150 lines missed.
    
  1. # frozen_string_literal: true
  2. # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
  3. # bugrep: Andreas Zehnder
  4. 1 require_relative '../../../rack'
  5. 1 require 'time'
  6. 1 require 'securerandom'
  7. 1 require 'digest/sha2'
  8. 1 module Rack
  9. 1 module Session
  10. 1 class SessionId
  11. 1 ID_VERSION = 2
  12. 1 attr_reader :public_id
  13. 1 def initialize(public_id)
  14. @public_id = public_id
  15. end
  16. 1 def private_id
  17. "#{ID_VERSION}::#{hash_sid(public_id)}"
  18. end
  19. 1 alias :cookie_value :public_id
  20. 1 alias :to_s :public_id
  21. 1 def empty?; false; end
  22. 1 def inspect; public_id.inspect; end
  23. 1 private
  24. 1 def hash_sid(sid)
  25. Digest::SHA256.hexdigest(sid)
  26. end
  27. end
  28. 1 module Abstract
  29. # SessionHash is responsible to lazily load the session from store.
  30. 1 class SessionHash
  31. 1 include Enumerable
  32. 1 attr_writer :id
  33. 1 Unspecified = Object.new
  34. 1 def self.find(req)
  35. req.get_header RACK_SESSION
  36. end
  37. 1 def self.set(req, session)
  38. req.set_header RACK_SESSION, session
  39. end
  40. 1 def self.set_options(req, options)
  41. req.set_header RACK_SESSION_OPTIONS, options.dup
  42. end
  43. 1 def initialize(store, req)
  44. @store = store
  45. @req = req
  46. @loaded = false
  47. end
  48. 1 def id
  49. return @id if @loaded or instance_variable_defined?(:@id)
  50. @id = @store.send(:extract_session_id, @req)
  51. end
  52. 1 def options
  53. @req.session_options
  54. end
  55. 1 def each(&block)
  56. load_for_read!
  57. @data.each(&block)
  58. end
  59. 1 def [](key)
  60. load_for_read!
  61. @data[key.to_s]
  62. end
  63. 1 def dig(key, *keys)
  64. load_for_read!
  65. @data.dig(key.to_s, *keys)
  66. end
  67. 1 def fetch(key, default = Unspecified, &block)
  68. load_for_read!
  69. if default == Unspecified
  70. @data.fetch(key.to_s, &block)
  71. else
  72. @data.fetch(key.to_s, default, &block)
  73. end
  74. end
  75. 1 def has_key?(key)
  76. load_for_read!
  77. @data.has_key?(key.to_s)
  78. end
  79. 1 alias :key? :has_key?
  80. 1 alias :include? :has_key?
  81. 1 def []=(key, value)
  82. load_for_write!
  83. @data[key.to_s] = value
  84. end
  85. 1 alias :store :[]=
  86. 1 def clear
  87. load_for_write!
  88. @data.clear
  89. end
  90. 1 def destroy
  91. clear
  92. @id = @store.send(:delete_session, @req, id, options)
  93. end
  94. 1 def to_hash
  95. load_for_read!
  96. @data.dup
  97. end
  98. 1 def update(hash)
  99. load_for_write!
  100. @data.update(stringify_keys(hash))
  101. end
  102. 1 alias :merge! :update
  103. 1 def replace(hash)
  104. load_for_write!
  105. @data.replace(stringify_keys(hash))
  106. end
  107. 1 def delete(key)
  108. load_for_write!
  109. @data.delete(key.to_s)
  110. end
  111. 1 def inspect
  112. if loaded?
  113. @data.inspect
  114. else
  115. "#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>"
  116. end
  117. end
  118. 1 def exists?
  119. return @exists if instance_variable_defined?(:@exists)
  120. @data = {}
  121. @exists = @store.send(:session_exists?, @req)
  122. end
  123. 1 def loaded?
  124. @loaded
  125. end
  126. 1 def empty?
  127. load_for_read!
  128. @data.empty?
  129. end
  130. 1 def keys
  131. load_for_read!
  132. @data.keys
  133. end
  134. 1 def values
  135. load_for_read!
  136. @data.values
  137. end
  138. 1 private
  139. 1 def load_for_read!
  140. load! if !loaded? && exists?
  141. end
  142. 1 def load_for_write!
  143. load! unless loaded?
  144. end
  145. 1 def load!
  146. @id, session = @store.send(:load_session, @req)
  147. @data = stringify_keys(session)
  148. @loaded = true
  149. end
  150. 1 def stringify_keys(other)
  151. # Use transform_keys after dropping Ruby 2.4 support
  152. hash = {}
  153. other.to_hash.each do |key, value|
  154. hash[key.to_s] = value
  155. end
  156. hash
  157. end
  158. end
  159. # ID sets up a basic framework for implementing an id based sessioning
  160. # service. Cookies sent to the client for maintaining sessions will only
  161. # contain an id reference. Only #find_session, #write_session and
  162. # #delete_session are required to be overwritten.
  163. #
  164. # All parameters are optional.
  165. # * :key determines the name of the cookie, by default it is
  166. # 'rack.session'
  167. # * :path, :domain, :expire_after, :secure, and :httponly set the related
  168. # cookie options as by Rack::Response#set_cookie
  169. # * :skip will not a set a cookie in the response nor update the session state
  170. # * :defer will not set a cookie in the response but still update the session
  171. # state if it is used with a backend
  172. # * :renew (implementation dependent) will prompt the generation of a new
  173. # session id, and migration of data to be referenced at the new id. If
  174. # :defer is set, it will be overridden and the cookie will be set.
  175. # * :sidbits sets the number of bits in length that a generated session
  176. # id will be.
  177. #
  178. # These options can be set on a per request basis, at the location of
  179. # <tt>env['rack.session.options']</tt>. Additionally the id of the
  180. # session can be found within the options hash at the key :id. It is
  181. # highly not recommended to change its value.
  182. #
  183. # Is Rack::Utils::Context compatible.
  184. #
  185. # Not included by default; you must require 'rack/session/abstract/id'
  186. # to use.
  187. 1 class Persisted
  188. DEFAULT_OPTIONS = {
  189. 1 key: RACK_SESSION,
  190. path: '/',
  191. domain: nil,
  192. expire_after: nil,
  193. secure: false,
  194. httponly: true,
  195. defer: false,
  196. renew: false,
  197. sidbits: 128,
  198. cookie_only: true,
  199. secure_random: ::SecureRandom
  200. }.freeze
  201. 1 attr_reader :key, :default_options, :sid_secure
  202. 1 def initialize(app, options = {})
  203. @app = app
  204. @default_options = self.class::DEFAULT_OPTIONS.merge(options)
  205. @key = @default_options.delete(:key)
  206. @cookie_only = @default_options.delete(:cookie_only)
  207. @same_site = @default_options.delete(:same_site)
  208. initialize_sid
  209. end
  210. 1 def call(env)
  211. context(env)
  212. end
  213. 1 def context(env, app = @app)
  214. req = make_request env
  215. prepare_session(req)
  216. status, headers, body = app.call(req.env)
  217. res = Rack::Response::Raw.new status, headers
  218. commit_session(req, res)
  219. [status, headers, body]
  220. end
  221. 1 private
  222. 1 def make_request(env)
  223. Rack::Request.new env
  224. end
  225. 1 def initialize_sid
  226. @sidbits = @default_options[:sidbits]
  227. @sid_secure = @default_options[:secure_random]
  228. @sid_length = @sidbits / 4
  229. end
  230. # Generate a new session id using Ruby #rand. The size of the
  231. # session id is controlled by the :sidbits option.
  232. # Monkey patch this to use custom methods for session id generation.
  233. 1 def generate_sid(secure = @sid_secure)
  234. if secure
  235. secure.hex(@sid_length)
  236. else
  237. "%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1)
  238. end
  239. rescue NotImplementedError
  240. generate_sid(false)
  241. end
  242. # Sets the lazy session at 'rack.session' and places options and session
  243. # metadata into 'rack.session.options'.
  244. 1 def prepare_session(req)
  245. session_was = req.get_header RACK_SESSION
  246. session = session_class.new(self, req)
  247. req.set_header RACK_SESSION, session
  248. req.set_header RACK_SESSION_OPTIONS, @default_options.dup
  249. session.merge! session_was if session_was
  250. end
  251. # Extracts the session id from provided cookies and passes it and the
  252. # environment to #find_session.
  253. 1 def load_session(req)
  254. sid = current_session_id(req)
  255. sid, session = find_session(req, sid)
  256. [sid, session || {}]
  257. end
  258. # Extract session id from request object.
  259. 1 def extract_session_id(request)
  260. sid = request.cookies[@key]
  261. sid ||= request.params[@key] unless @cookie_only
  262. sid
  263. end
  264. # Returns the current session id from the SessionHash.
  265. 1 def current_session_id(req)
  266. req.get_header(RACK_SESSION).id
  267. end
  268. # Check if the session exists or not.
  269. 1 def session_exists?(req)
  270. value = current_session_id(req)
  271. value && !value.empty?
  272. end
  273. # Session should be committed if it was loaded, any of specific options like :renew, :drop
  274. # or :expire_after was given and the security permissions match. Skips if skip is given.
  275. 1 def commit_session?(req, session, options)
  276. if options[:skip]
  277. false
  278. else
  279. has_session = loaded_session?(session) || forced_session_update?(session, options)
  280. has_session && security_matches?(req, options)
  281. end
  282. end
  283. 1 def loaded_session?(session)
  284. !session.is_a?(session_class) || session.loaded?
  285. end
  286. 1 def forced_session_update?(session, options)
  287. force_options?(options) && session && !session.empty?
  288. end
  289. 1 def force_options?(options)
  290. options.values_at(:max_age, :renew, :drop, :defer, :expire_after).any?
  291. end
  292. 1 def security_matches?(request, options)
  293. return true unless options[:secure]
  294. request.ssl?
  295. end
  296. # Acquires the session from the environment and the session id from
  297. # the session options and passes them to #write_session. If successful
  298. # and the :defer option is not true, a cookie will be added to the
  299. # response with the session's id.
  300. 1 def commit_session(req, res)
  301. session = req.get_header RACK_SESSION
  302. options = session.options
  303. if options[:drop] || options[:renew]
  304. session_id = delete_session(req, session.id || generate_sid, options)
  305. return unless session_id
  306. end
  307. return unless commit_session?(req, session, options)
  308. session.send(:load!) unless loaded_session?(session)
  309. session_id ||= session.id
  310. session_data = session.to_hash.delete_if { |k, v| v.nil? }
  311. if not data = write_session(req, session_id, session_data, options)
  312. req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
  313. elsif options[:defer] and not options[:renew]
  314. req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
  315. else
  316. cookie = Hash.new
  317. cookie[:value] = cookie_value(data)
  318. cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
  319. cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
  320. if @same_site.respond_to? :call
  321. cookie[:same_site] = @same_site.call(req, res)
  322. else
  323. cookie[:same_site] = @same_site
  324. end
  325. set_cookie(req, res, cookie.merge!(options))
  326. end
  327. end
  328. 1 public :commit_session
  329. 1 def cookie_value(data)
  330. data
  331. end
  332. # Sets the cookie back to the client with session id. We skip the cookie
  333. # setting if the value didn't change (sid is the same) or expires was given.
  334. 1 def set_cookie(request, res, cookie)
  335. if request.cookies[@key] != cookie[:value] || cookie[:expires]
  336. res.set_cookie_header =
  337. Utils.add_cookie_to_header(res.set_cookie_header, @key, cookie)
  338. end
  339. end
  340. # Allow subclasses to prepare_session for different Session classes
  341. 1 def session_class
  342. SessionHash
  343. end
  344. # All thread safety and session retrieval procedures should occur here.
  345. # Should return [session_id, session].
  346. # If nil is provided as the session id, generation of a new valid id
  347. # should occur within.
  348. 1 def find_session(env, sid)
  349. raise '#find_session not implemented.'
  350. end
  351. # All thread safety and session storage procedures should occur here.
  352. # Must return the session id if the session was saved successfully, or
  353. # false if the session could not be saved.
  354. 1 def write_session(req, sid, session, options)
  355. raise '#write_session not implemented.'
  356. end
  357. # All thread safety and session destroy procedures should occur here.
  358. # Should return a new session id or nil if options[:drop]
  359. 1 def delete_session(req, sid, options)
  360. raise '#delete_session not implemented'
  361. end
  362. end
  363. 1 class PersistedSecure < Persisted
  364. 1 class SecureSessionHash < SessionHash
  365. 1 def [](key)
  366. if key == "session_id"
  367. load_for_read!
  368. id.public_id if id
  369. else
  370. super
  371. end
  372. end
  373. end
  374. 1 def generate_sid(*)
  375. public_id = super
  376. SessionId.new(public_id)
  377. end
  378. 1 def extract_session_id(*)
  379. public_id = super
  380. public_id && SessionId.new(public_id)
  381. end
  382. 1 private
  383. 1 def session_class
  384. SecureSessionHash
  385. end
  386. 1 def cookie_value(data)
  387. data.cookie_value
  388. end
  389. end
  390. 1 class ID < Persisted
  391. 1 def self.inherited(klass)
  392. k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
  393. unless k.instance_variable_defined?(:"@_rack_warned")
  394. warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
  395. k.instance_variable_set(:"@_rack_warned", true)
  396. end
  397. super
  398. end
  399. # All thread safety and session retrieval procedures should occur here.
  400. # Should return [session_id, session].
  401. # If nil is provided as the session id, generation of a new valid id
  402. # should occur within.
  403. 1 def find_session(req, sid)
  404. get_session req.env, sid
  405. end
  406. # All thread safety and session storage procedures should occur here.
  407. # Must return the session id if the session was saved successfully, or
  408. # false if the session could not be saved.
  409. 1 def write_session(req, sid, session, options)
  410. set_session req.env, sid, session, options
  411. end
  412. # All thread safety and session destroy procedures should occur here.
  413. # Should return a new session id or nil if options[:drop]
  414. 1 def delete_session(req, sid, options)
  415. destroy_session req.env, sid, options
  416. end
  417. end
  418. end
  419. end
  420. end

vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails-dom-testing.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 require 'rails/dom/testing/assertions'

vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions.rb

100.0% lines covered

11 relevant lines. 11 lines covered and 0 lines missed.
    
  1. 1 require 'active_support/concern'
  2. 1 require 'nokogiri'
  3. 1 module Rails
  4. 1 module Dom
  5. 1 module Testing
  6. 1 module Assertions
  7. 1 autoload :DomAssertions, 'rails/dom/testing/assertions/dom_assertions'
  8. 1 autoload :SelectorAssertions, 'rails/dom/testing/assertions/selector_assertions'
  9. 1 extend ActiveSupport::Concern
  10. 1 include DomAssertions
  11. 1 include SelectorAssertions
  12. end
  13. end
  14. end
  15. end

vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/dom_assertions.rb

38.89% lines covered

36 relevant lines. 14 lines covered and 22 lines missed.
    
  1. 1 module Rails
  2. 1 module Dom
  3. 1 module Testing
  4. 1 module Assertions
  5. 1 module DomAssertions
  6. # \Test two HTML strings for equivalency (e.g., equal even when attributes are in another order)
  7. #
  8. # # assert that the referenced method generates the appropriate HTML string
  9. # assert_dom_equal '<a href="http://www.example.com">Apples</a>', link_to("Apples", "http://www.example.com")
  10. 1 def assert_dom_equal(expected, actual, message = nil)
  11. expected_dom, actual_dom = fragment(expected), fragment(actual)
  12. message ||= "Expected: #{expected}\nActual: #{actual}"
  13. assert compare_doms(expected_dom, actual_dom), message
  14. end
  15. # The negated form of +assert_dom_equal+.
  16. #
  17. # # assert that the referenced method does not generate the specified HTML string
  18. # assert_dom_not_equal '<a href="http://www.example.com">Apples</a>', link_to("Oranges", "http://www.example.com")
  19. 1 def assert_dom_not_equal(expected, actual, message = nil)
  20. expected_dom, actual_dom = fragment(expected), fragment(actual)
  21. message ||= "Expected: #{expected}\nActual: #{actual}"
  22. assert_not compare_doms(expected_dom, actual_dom), message
  23. end
  24. 1 protected
  25. 1 def compare_doms(expected, actual)
  26. return false unless expected.children.size == actual.children.size
  27. expected.children.each_with_index do |child, i|
  28. return false unless equal_children?(child, actual.children[i])
  29. end
  30. true
  31. end
  32. 1 def equal_children?(child, other_child)
  33. return false unless child.type == other_child.type
  34. if child.element?
  35. child.name == other_child.name &&
  36. equal_attribute_nodes?(child.attribute_nodes, other_child.attribute_nodes) &&
  37. compare_doms(child, other_child)
  38. else
  39. child.to_s == other_child.to_s
  40. end
  41. end
  42. 1 def equal_attribute_nodes?(nodes, other_nodes)
  43. return false unless nodes.size == other_nodes.size
  44. nodes = nodes.sort_by(&:name)
  45. other_nodes = other_nodes.sort_by(&:name)
  46. nodes.each_with_index do |attr, i|
  47. return false unless equal_attribute?(attr, other_nodes[i])
  48. end
  49. true
  50. end
  51. 1 def equal_attribute?(attr, other_attr)
  52. attr.name == other_attr.name && attr.value == other_attr.value
  53. end
  54. 1 private
  55. 1 def fragment(text)
  56. Nokogiri::HTML::DocumentFragment.parse(text)
  57. end
  58. end
  59. end
  60. end
  61. end
  62. end

vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions.rb

30.51% lines covered

59 relevant lines. 18 lines covered and 41 lines missed.
    
  1. 1 require 'active_support/deprecation'
  2. 1 require_relative 'selector_assertions/count_describable'
  3. 1 require_relative 'selector_assertions/html_selector'
  4. 1 module Rails
  5. 1 module Dom
  6. 1 module Testing
  7. 1 module Assertions
  8. # Adds the +assert_select+ method for use in Rails functional
  9. # test cases, which can be used to make assertions on the response HTML of a controller
  10. # action. You can also call +assert_select+ within another +assert_select+ to
  11. # make assertions on elements selected by the enclosing assertion.
  12. #
  13. # Use +css_select+ to select elements without making an assertions, either
  14. # from the response HTML or elements selected by the enclosing assertion.
  15. #
  16. # In addition to HTML responses, you can make the following assertions:
  17. #
  18. # * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
  19. # * +assert_select_email+ - Assertions on the HTML body of an e-mail.
  20. 1 module SelectorAssertions
  21. # Select and return all matching elements.
  22. #
  23. # If called with a single argument, uses that argument as a selector.
  24. # Called without an element +css_select+ selects from
  25. # the element returned in +document_root_element+
  26. #
  27. # The default implementation of +document_root_element+ raises an exception explaining this.
  28. #
  29. # Returns an empty Nokogiri::XML::NodeSet if no match is found.
  30. #
  31. # If called with two arguments, uses the first argument as the root
  32. # element and the second argument as the selector. Attempts to match the
  33. # root element and any of its children.
  34. # Returns an empty Nokogiri::XML::NodeSet if no match is found.
  35. #
  36. # The selector may be a CSS selector expression (String).
  37. # css_select returns nil if called with an invalid css selector.
  38. #
  39. # # Selects all div tags
  40. # divs = css_select("div")
  41. #
  42. # # Selects all paragraph tags and does something interesting
  43. # pars = css_select("p")
  44. # pars.each do |par|
  45. # # Do something fun with paragraphs here...
  46. # end
  47. #
  48. # # Selects all list items in unordered lists
  49. # items = css_select("ul>li")
  50. #
  51. # # Selects all form tags and then all inputs inside the form
  52. # forms = css_select("form")
  53. # forms.each do |form|
  54. # inputs = css_select(form, "input")
  55. # ...
  56. # end
  57. 1 def css_select(*args)
  58. raise ArgumentError, "you at least need a selector argument" if args.empty?
  59. root = args.size == 1 ? document_root_element : args.shift
  60. nodeset(root).css(args.first)
  61. end
  62. # An assertion that selects elements and makes one or more equality tests.
  63. #
  64. # If the first argument is an element, selects all matching elements
  65. # starting from (and including) that element and all its children in
  66. # depth-first order.
  67. #
  68. # If no element is specified +assert_select+ selects from
  69. # the element returned in +document_root_element+
  70. # unless +assert_select+ is called from within an +assert_select+ block.
  71. # Override +document_root_element+ to tell +assert_select+ what to select from.
  72. # The default implementation raises an exception explaining this.
  73. #
  74. # When called with a block +assert_select+ passes an array of selected elements
  75. # to the block. Calling +assert_select+ from the block, with no element specified,
  76. # runs the assertion on the complete set of elements selected by the enclosing assertion.
  77. # Alternatively the array may be iterated through so that +assert_select+ can be called
  78. # separately for each element.
  79. #
  80. #
  81. # ==== Example
  82. # If the response contains two ordered lists, each with four list elements then:
  83. # assert_select "ol" do |elements|
  84. # elements.each do |element|
  85. # assert_select element, "li", 4
  86. # end
  87. # end
  88. #
  89. # will pass, as will:
  90. # assert_select "ol" do
  91. # assert_select "li", 8
  92. # end
  93. #
  94. # The selector may be a CSS selector expression (String) or an expression
  95. # with substitution values (Array).
  96. # Substitution uses a custom pseudo class match. Pass in whatever attribute you want to match (enclosed in quotes) and a ? for the substitution.
  97. # assert_select returns nil if called with an invalid css selector.
  98. #
  99. # assert_select "div:match('id', ?)", /\d+/
  100. #
  101. # === Equality Tests
  102. #
  103. # The equality test may be one of the following:
  104. # * <tt>true</tt> - Assertion is true if at least one element selected.
  105. # * <tt>false</tt> - Assertion is true if no element selected.
  106. # * <tt>String/Regexp</tt> - Assertion is true if the text value of at least
  107. # one element matches the string or regular expression.
  108. # * <tt>Integer</tt> - Assertion is true if exactly that number of
  109. # elements are selected.
  110. # * <tt>Range</tt> - Assertion is true if the number of selected
  111. # elements fit the range.
  112. # If no equality test specified, the assertion is true if at least one
  113. # element selected.
  114. #
  115. # To perform more than one equality tests, use a hash with the following keys:
  116. # * <tt>:text</tt> - Narrow the selection to elements that have this text
  117. # value (string or regexp).
  118. # * <tt>:html</tt> - Narrow the selection to elements that have this HTML
  119. # content (string or regexp).
  120. # * <tt>:count</tt> - Assertion is true if the number of selected elements
  121. # is equal to this value.
  122. # * <tt>:minimum</tt> - Assertion is true if the number of selected
  123. # elements is at least this value.
  124. # * <tt>:maximum</tt> - Assertion is true if the number of selected
  125. # elements is at most this value.
  126. #
  127. # If the method is called with a block, once all equality tests are
  128. # evaluated the block is called with an array of all matched elements.
  129. #
  130. # # At least one form element
  131. # assert_select "form"
  132. #
  133. # # Form element includes four input fields
  134. # assert_select "form input", 4
  135. #
  136. # # Page title is "Welcome"
  137. # assert_select "title", "Welcome"
  138. #
  139. # # Page title is "Welcome" and there is only one title element
  140. # assert_select "title", {count: 1, text: "Welcome"},
  141. # "Wrong title or more than one title element"
  142. #
  143. # # Page contains no forms
  144. # assert_select "form", false, "This page must contain no forms"
  145. #
  146. # # Test the content and style
  147. # assert_select "body div.header ul.menu"
  148. #
  149. # # Use substitution values
  150. # assert_select "ol>li:match('id', ?)", /item-\d+/
  151. #
  152. # # All input fields in the form have a name
  153. # assert_select "form input" do
  154. # assert_select ":match('name', ?)", /.+/ # Not empty
  155. # end
  156. 1 def assert_select(*args, &block)
  157. @selected ||= nil
  158. selector = HTMLSelector.new(args, @selected) { nodeset document_root_element }
  159. if selector.selecting_no_body?
  160. assert true
  161. return
  162. end
  163. selector.select.tap do |matches|
  164. assert_size_match!(matches.size, selector.tests,
  165. selector.css_selector, selector.message)
  166. nest_selection(matches, &block) if block_given? && !matches.empty?
  167. end
  168. end
  169. # Extracts the content of an element, treats it as encoded HTML and runs
  170. # nested assertion on it.
  171. #
  172. # You typically call this method within another assertion to operate on
  173. # all currently selected elements. You can also pass an element or array
  174. # of elements.
  175. #
  176. # The content of each element is un-encoded, and wrapped in the root
  177. # element +encoded+. It then calls the block with all un-encoded elements.
  178. #
  179. # # Selects all bold tags from within the title of an Atom feed's entries (perhaps to nab a section name prefix)
  180. # assert_select "feed[xmlns='http://www.w3.org/2005/Atom']" do
  181. # # Select each entry item and then the title item
  182. # assert_select "entry>title" do
  183. # # Run assertions on the encoded title elements
  184. # assert_select_encoded do
  185. # assert_select "b"
  186. # end
  187. # end
  188. # end
  189. #
  190. #
  191. # # Selects all paragraph tags from within the description of an RSS feed
  192. # assert_select "rss[version=2.0]" do
  193. # # Select description element of each feed item.
  194. # assert_select "channel>item>description" do
  195. # # Run assertions on the encoded elements.
  196. # assert_select_encoded do
  197. # assert_select "p"
  198. # end
  199. # end
  200. # end
  201. 1 def assert_select_encoded(element = nil, &block)
  202. if !element && !@selected
  203. raise ArgumentError, "Element is required when called from a nonnested assert_select"
  204. end
  205. content = nodeset(element || @selected).map do |elem|
  206. elem.children.select do |child|
  207. child.cdata? || (child.text? && !child.blank?)
  208. end.map(&:content)
  209. end.join
  210. selected = Nokogiri::HTML::DocumentFragment.parse(content)
  211. nest_selection(selected) do
  212. if content.empty?
  213. yield selected
  214. else
  215. assert_select ":root", &block
  216. end
  217. end
  218. end
  219. # Extracts the body of an email and runs nested assertions on it.
  220. #
  221. # You must enable deliveries for this assertion to work, use:
  222. # ActionMailer::Base.perform_deliveries = true
  223. #
  224. # assert_select_email do
  225. # assert_select "h1", "Email alert"
  226. # end
  227. #
  228. # assert_select_email do
  229. # items = assert_select "ol>li"
  230. # items.each do
  231. # # Work with items here...
  232. # end
  233. # end
  234. 1 def assert_select_email(&block)
  235. deliveries = ActionMailer::Base.deliveries
  236. assert !deliveries.empty?, "No e-mail in delivery list"
  237. deliveries.each do |delivery|
  238. (delivery.parts.empty? ? [delivery] : delivery.parts).each do |part|
  239. if part["Content-Type"].to_s =~ /^text\/html\W/
  240. root = Nokogiri::HTML::DocumentFragment.parse(part.body.to_s)
  241. assert_select root, ":root", &block
  242. end
  243. end
  244. end
  245. end
  246. 1 private
  247. 1 include CountDescribable
  248. 1 def document_root_element
  249. raise NotImplementedError, 'Implementing document_root_element makes ' \
  250. 'assert_select work without needing to specify an element to select from.'
  251. end
  252. # +equals+ must contain :minimum, :maximum and :count keys
  253. 1 def assert_size_match!(size, equals, css_selector, message = nil)
  254. min, max, count = equals[:minimum], equals[:maximum], equals[:count]
  255. message ||= %(Expected #{count_description(min, max, count)} matching "#{css_selector}", found #{size}.)
  256. if count
  257. assert_equal count, size, message
  258. else
  259. assert_operator size, :>=, min, message if min
  260. assert_operator size, :<=, max, message if max
  261. end
  262. end
  263. 1 def nest_selection(selection)
  264. # Set @selected to allow nested assert_select.
  265. # Can be nested several levels deep.
  266. old_selected, @selected = @selected, selection
  267. yield @selected
  268. ensure
  269. @selected = old_selected
  270. end
  271. 1 def nodeset(node)
  272. if node.is_a?(Nokogiri::XML::NodeSet)
  273. node
  274. else
  275. Nokogiri::XML::NodeSet.new(node.document, [node])
  276. end
  277. end
  278. end
  279. end
  280. end
  281. end
  282. end

vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/count_describable.rb

55.0% lines covered

20 relevant lines. 11 lines covered and 9 lines missed.
    
  1. 1 require 'active_support/concern'
  2. 1 module Rails
  3. 1 module Dom
  4. 1 module Testing
  5. 1 module Assertions
  6. 1 module SelectorAssertions
  7. 1 module CountDescribable
  8. 1 extend ActiveSupport::Concern
  9. 1 private
  10. 1 def count_description(min, max, count) #:nodoc:
  11. if min && max && (max != min)
  12. "between #{min} and #{max} elements"
  13. elsif min && max && max == min && count
  14. "exactly #{count} #{pluralize_element(min)}"
  15. elsif min && !(min == 1 && max == 1)
  16. "at least #{min} #{pluralize_element(min)}"
  17. elsif max
  18. "at most #{max} #{pluralize_element(max)}"
  19. end
  20. end
  21. 1 def pluralize_element(quantity)
  22. quantity == 1 ? 'element' : 'elements'
  23. end
  24. end
  25. end
  26. end
  27. end
  28. end
  29. end

vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/html_selector.rb

21.54% lines covered

65 relevant lines. 14 lines covered and 51 lines missed.
    
  1. 1 require 'active_support/core_ext/module/attribute_accessors'
  2. 1 require_relative 'substitution_context'
  3. 1 class HTMLSelector #:nodoc:
  4. 1 attr_reader :css_selector, :tests, :message
  5. 1 def initialize(values, previous_selection = nil, &root_fallback)
  6. @values = values
  7. @root = extract_root(previous_selection, root_fallback)
  8. extract_selectors
  9. @tests = extract_equality_tests
  10. @message = @values.shift
  11. if @values.shift
  12. raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
  13. end
  14. end
  15. 1 def selecting_no_body? #:nodoc:
  16. # Nokogiri gives the document a body element. Which means we can't
  17. # run an assertion expecting there to not be a body.
  18. @selector == 'body' && @tests[:count] == 0
  19. end
  20. 1 def select
  21. filter @root.css(@selector, context)
  22. end
  23. 1 private
  24. 1 NO_STRIP = %w{pre script style textarea}
  25. 2 mattr_reader(:context) { SubstitutionContext.new }
  26. 1 def filter(matches)
  27. match_with = tests[:text] || tests[:html]
  28. return matches if matches.empty? || !match_with
  29. content_mismatch = nil
  30. text_matches = tests.has_key?(:text)
  31. regex_matching = match_with.is_a?(Regexp)
  32. remaining = matches.reject do |match|
  33. # Preserve markup with to_s for html elements
  34. content = text_matches ? match.text : match.children.to_s
  35. content.strip! unless NO_STRIP.include?(match.name)
  36. content.sub!(/\A\n/, '') if text_matches && match.name == "textarea"
  37. next if regex_matching ? (content =~ match_with) : (content == match_with)
  38. content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, content)
  39. true
  40. end
  41. @message ||= content_mismatch if remaining.empty?
  42. Nokogiri::XML::NodeSet.new(matches.document, remaining)
  43. end
  44. 1 def extract_root(previous_selection, root_fallback)
  45. possible_root = @values.first
  46. if possible_root == nil
  47. raise ArgumentError, 'First argument is either selector or element ' \
  48. 'to select, but nil found. Perhaps you called assert_select with ' \
  49. 'an element that does not exist?'
  50. elsif possible_root.respond_to?(:css)
  51. @values.shift # remove the root, so selector is the first argument
  52. possible_root
  53. elsif previous_selection
  54. previous_selection
  55. else
  56. root_fallback.call
  57. end
  58. end
  59. 1 def extract_selectors
  60. selector = @values.shift
  61. unless selector.is_a? String
  62. raise ArgumentError, "Expecting a selector as the first argument"
  63. end
  64. @css_selector = context.substitute!(selector, @values.dup, true)
  65. @selector = context.substitute!(selector, @values)
  66. end
  67. 1 def extract_equality_tests
  68. comparisons = {}
  69. case comparator = @values.shift
  70. when Hash
  71. comparisons = comparator
  72. when String, Regexp
  73. comparisons[:text] = comparator
  74. when Integer
  75. comparisons[:count] = comparator
  76. when Range
  77. comparisons[:minimum] = comparator.begin
  78. comparisons[:maximum] = comparator.end
  79. when FalseClass
  80. comparisons[:count] = 0
  81. when NilClass, TrueClass
  82. comparisons[:minimum] = 1
  83. else raise ArgumentError, "I don't understand what you're trying to match"
  84. end
  85. # By default we're looking for at least one match.
  86. if comparisons[:count]
  87. comparisons[:minimum] = comparisons[:maximum] = comparisons[:count]
  88. else
  89. comparisons[:minimum] ||= 1
  90. end
  91. comparisons
  92. end
  93. end

vender/bundle/ruby/2.6.0/gems/rails-dom-testing-2.0.3/lib/rails/dom/testing/assertions/selector_assertions/substitution_context.rb

47.06% lines covered

17 relevant lines. 8 lines covered and 9 lines missed.
    
  1. 1 class SubstitutionContext
  2. 1 def initialize
  3. 1 @substitute = '?'
  4. end
  5. 1 def substitute!(selector, values, format_for_presentation = false)
  6. selector = selector.dup
  7. while !values.empty? && substitutable?(values.first) && selector.index(@substitute)
  8. selector.sub! @substitute, matcher_for(values.shift, format_for_presentation)
  9. end
  10. selector
  11. end
  12. 1 def match(matches, attribute, matcher)
  13. matches.find_all { |node| node[attribute] =~ Regexp.new(matcher) }
  14. end
  15. 1 private
  16. 1 def matcher_for(value, format_for_presentation)
  17. # Nokogiri doesn't like arbitrary values without quotes, hence inspect.
  18. if format_for_presentation
  19. value.inspect # Avoid to_s so Regexps aren't put in quotes.
  20. else
  21. value.to_s.inspect
  22. end
  23. end
  24. 1 def substitutable?(value)
  25. value.is_a?(String) || value.is_a?(Regexp)
  26. end
  27. end

vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/base_formatter.rb

92.31% lines covered

26 relevant lines. 24 lines covered and 2 lines missed.
    
  1. 1 RSpec::Support.require_rspec_core "formatters/helpers"
  2. 1 require 'stringio'
  3. 1 module RSpec
  4. 1 module Core
  5. 1 module Formatters
  6. # RSpec's built-in formatters are all subclasses of
  7. # RSpec::Core::Formatters::BaseFormatter.
  8. #
  9. # @see RSpec::Core::Formatters::BaseTextFormatter
  10. # @see RSpec::Core::Reporter
  11. # @see RSpec::Core::Formatters::Protocol
  12. 1 class BaseFormatter
  13. # All formatters inheriting from this formatter will receive these
  14. # notifications.
  15. 1 Formatters.register self, :start, :example_group_started, :close
  16. 1 attr_accessor :example_group
  17. 1 attr_reader :output
  18. # @api public
  19. # @param output [IO] the formatter output
  20. # @see RSpec::Core::Formatters::Protocol#initialize
  21. 1 def initialize(output)
  22. 1 @output = output || StringIO.new
  23. 1 @example_group = nil
  24. end
  25. # @api public
  26. #
  27. # @param notification [StartNotification]
  28. # @see RSpec::Core::Formatters::Protocol#start
  29. 1 def start(notification)
  30. 1 start_sync_output
  31. 1 @example_count = notification.count
  32. end
  33. # @api public
  34. #
  35. # @param notification [GroupNotification] containing example_group
  36. # subclass of `RSpec::Core::ExampleGroup`
  37. # @see RSpec::Core::Formatters::Protocol#example_group_started
  38. 1 def example_group_started(notification)
  39. 21 @example_group = notification.group
  40. end
  41. # @api public
  42. #
  43. # @param _notification [NullNotification] (Ignored)
  44. # @see RSpec::Core::Formatters::Protocol#close
  45. 1 def close(_notification)
  46. restore_sync_output
  47. end
  48. 1 private
  49. 1 def start_sync_output
  50. 1 @old_sync, output.sync = output.sync, true if output_supports_sync
  51. end
  52. 1 def restore_sync_output
  53. output.sync = @old_sync if output_supports_sync && !output.closed?
  54. end
  55. 1 def output_supports_sync
  56. 1 output.respond_to?(:sync=)
  57. end
  58. end
  59. end
  60. end
  61. end

vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/base_text_formatter.rb

91.3% lines covered

23 relevant lines. 21 lines covered and 2 lines missed.
    
  1. 1 RSpec::Support.require_rspec_core "formatters/base_formatter"
  2. 1 module RSpec
  3. 1 module Core
  4. 1 module Formatters
  5. # Base for all of RSpec's built-in formatters. See
  6. # RSpec::Core::Formatters::BaseFormatter to learn more about all of the
  7. # methods called by the reporter.
  8. #
  9. # @see RSpec::Core::Formatters::BaseFormatter
  10. # @see RSpec::Core::Reporter
  11. 1 class BaseTextFormatter < BaseFormatter
  12. 1 Formatters.register self,
  13. :message, :dump_summary, :dump_failures, :dump_pending, :seed
  14. # @api public
  15. #
  16. # Used by the reporter to send messages to the output stream.
  17. #
  18. # @param notification [MessageNotification] containing message
  19. 1 def message(notification)
  20. output.puts notification.message
  21. end
  22. # @api public
  23. #
  24. # Dumps detailed information about each example failure.
  25. #
  26. # @param notification [NullNotification]
  27. 1 def dump_failures(notification)
  28. 1 return if notification.failure_notifications.empty?
  29. 1 output.puts notification.fully_formatted_failed_examples
  30. end
  31. # @api public
  32. #
  33. # This method is invoked after the dumping of examples and failures.
  34. # Each parameter is assigned to a corresponding attribute.
  35. #
  36. # @param summary [SummaryNotification] containing duration,
  37. # example_count, failure_count and pending_count
  38. 1 def dump_summary(summary)
  39. 1 output.puts summary.fully_formatted
  40. end
  41. # @private
  42. 1 def dump_pending(notification)
  43. 1 return if notification.pending_examples.empty?
  44. 1 output.puts notification.fully_formatted_pending_examples
  45. end
  46. # @private
  47. 1 def seed(notification)
  48. 2 return unless notification.seed_used?
  49. output.puts notification.fully_formatted
  50. end
  51. # @api public
  52. #
  53. # Invoked at the end of a suite run. Allows the formatter to do any
  54. # tidying up, but be aware that formatter output streams may be used
  55. # elsewhere so don't actually close them.
  56. #
  57. # @param _notification [NullNotification] (Ignored)
  58. 1 def close(_notification)
  59. 1 return if output.closed?
  60. 1 output.puts
  61. 1 output.flush
  62. end
  63. end
  64. end
  65. end
  66. end

vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/formatters/progress_formatter.rb

100.0% lines covered

15 relevant lines. 15 lines covered and 0 lines missed.
    
  1. 1 RSpec::Support.require_rspec_core "formatters/base_text_formatter"
  2. 1 RSpec::Support.require_rspec_core "formatters/console_codes"
  3. 1 module RSpec
  4. 1 module Core
  5. 1 module Formatters
  6. # @private
  7. 1 class ProgressFormatter < BaseTextFormatter
  8. 1 Formatters.register self, :example_passed, :example_pending, :example_failed, :start_dump
  9. 1 def example_passed(_notification)
  10. 13 output.print ConsoleCodes.wrap('.', :success)
  11. end
  12. 1 def example_pending(_notification)
  13. 6 output.print ConsoleCodes.wrap('*', :pending)
  14. end
  15. 1 def example_failed(_notification)
  16. 3 output.print ConsoleCodes.wrap('F', :failure)
  17. end
  18. 1 def start_dump(_notification)
  19. 1 output.puts
  20. end
  21. end
  22. end
  23. end
  24. end

vender/bundle/ruby/2.6.0/gems/rspec-core-3.10.1/lib/rspec/core/profiler.rb

100.0% lines covered

19 relevant lines. 19 lines covered and 0 lines missed.
    
  1. 1 module RSpec
  2. 1 module Core
  3. # @private
  4. 1 class Profiler
  5. 1 NOTIFICATIONS = [:example_group_started, :example_group_finished, :example_started]
  6. 1 def initialize
  7. 12 @example_groups = Hash.new { |h, k| h[k] = { :count => 0 } }
  8. end
  9. 1 attr_reader :example_groups
  10. 1 def example_group_started(notification)
  11. 21 return unless notification.group.top_level?
  12. 11 @example_groups[notification.group][:start] = Time.now
  13. 11 @example_groups[notification.group][:description] = notification.group.top_level_description
  14. end
  15. 1 def example_group_finished(notification)
  16. 21 return unless notification.group.top_level?
  17. 11 group = @example_groups[notification.group]
  18. 11 return unless group.key?(:start)
  19. 11 group[:total_time] = Time.now - group[:start]
  20. end
  21. 1 def example_started(notification)
  22. 22 group = notification.example.example_group.parent_groups.last
  23. 22 @example_groups[group][:count] += 1
  24. end
  25. end
  26. end
  27. end

vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/be.rb

56.41% lines covered

78 relevant lines. 44 lines covered and 34 lines missed.
    
  1. 1 module RSpec
  2. 1 module Matchers
  3. 1 module BuiltIn
  4. # @api private
  5. # Provides the implementation for `be_truthy`.
  6. # Not intended to be instantiated directly.
  7. 1 class BeTruthy < BaseMatcher
  8. # @api private
  9. # @return [String]
  10. 1 def failure_message
  11. "expected: truthy value\n got: #{actual_formatted}"
  12. end
  13. # @api private
  14. # @return [String]
  15. 1 def failure_message_when_negated
  16. "expected: falsey value\n got: #{actual_formatted}"
  17. end
  18. 1 private
  19. 1 def match(_, actual)
  20. !!actual
  21. end
  22. end
  23. # @api private
  24. # Provides the implementation for `be_falsey`.
  25. # Not intended to be instantiated directly.
  26. 1 class BeFalsey < BaseMatcher
  27. # @api private
  28. # @return [String]
  29. 1 def failure_message
  30. "expected: falsey value\n got: #{actual_formatted}"
  31. end
  32. # @api private
  33. # @return [String]
  34. 1 def failure_message_when_negated
  35. "expected: truthy value\n got: #{actual_formatted}"
  36. end
  37. 1 private
  38. 1 def match(_, actual)
  39. !actual
  40. end
  41. end
  42. # @api private
  43. # Provides the implementation for `be_nil`.
  44. # Not intended to be instantiated directly.
  45. 1 class BeNil < BaseMatcher
  46. # @api private
  47. # @return [String]
  48. 1 def failure_message
  49. "expected: nil\n got: #{actual_formatted}"
  50. end
  51. # @api private
  52. # @return [String]
  53. 1 def failure_message_when_negated
  54. "expected: not nil\n got: nil"
  55. end
  56. 1 private
  57. 1 def match(_, actual)
  58. actual.nil?
  59. end
  60. end
  61. # @private
  62. 1 module BeHelpers
  63. 1 private
  64. 1 def args_to_s
  65. @args.empty? ? "" : parenthesize(inspected_args.join(', '))
  66. end
  67. 1 def parenthesize(string)
  68. "(#{string})"
  69. end
  70. 1 def inspected_args
  71. @args.map { |a| RSpec::Support::ObjectFormatter.format(a) }
  72. end
  73. 1 def expected_to_sentence
  74. EnglishPhrasing.split_words(@expected)
  75. end
  76. 1 def args_to_sentence
  77. EnglishPhrasing.list(@args)
  78. end
  79. end
  80. # @api private
  81. # Provides the implementation for `be`.
  82. # Not intended to be instantiated directly.
  83. 1 class Be < BaseMatcher
  84. 1 include BeHelpers
  85. 1 def initialize(*args)
  86. @args = args
  87. end
  88. # @api private
  89. # @return [String]
  90. 1 def failure_message
  91. "expected #{actual_formatted} to evaluate to true"
  92. end
  93. # @api private
  94. # @return [String]
  95. 1 def failure_message_when_negated
  96. "expected #{actual_formatted} to evaluate to false"
  97. end
  98. 1 [:==, :<, :<=, :>=, :>, :===, :=~].each do |operator|
  99. 7 define_method operator do |operand|
  100. BeComparedTo.new(operand, operator)
  101. end
  102. end
  103. 1 private
  104. 1 def match(_, actual)
  105. !!actual
  106. end
  107. end
  108. # @api private
  109. # Provides the implementation of `be <operator> value`.
  110. # Not intended to be instantiated directly.
  111. 1 class BeComparedTo < BaseMatcher
  112. 1 include BeHelpers
  113. 1 def initialize(operand, operator)
  114. @expected = operand
  115. @operator = operator
  116. @args = []
  117. end
  118. 1 def matches?(actual)
  119. perform_match(actual)
  120. rescue ArgumentError, NoMethodError
  121. false
  122. end
  123. 1 def does_not_match?(actual)
  124. !perform_match(actual)
  125. rescue ArgumentError, NoMethodError
  126. false
  127. end
  128. # @api private
  129. # @return [String]
  130. 1 def failure_message
  131. "expected: #{@operator} #{expected_formatted}\n" \
  132. " got: #{@operator.to_s.gsub(/./, ' ')} #{actual_formatted}"
  133. end
  134. # @api private
  135. # @return [String]
  136. 1 def failure_message_when_negated
  137. message = "`expect(#{actual_formatted}).not_to " \
  138. "be #{@operator} #{expected_formatted}`"
  139. if [:<, :>, :<=, :>=].include?(@operator)
  140. message + " not only FAILED, it is a bit confusing."
  141. else
  142. message
  143. end
  144. end
  145. # @api private
  146. # @return [String]
  147. 1 def description
  148. "be #{@operator} #{expected_to_sentence}#{args_to_sentence}"
  149. end
  150. 1 private
  151. 1 def perform_match(actual)
  152. @actual = actual
  153. @actual.__send__ @operator, @expected
  154. end
  155. end
  156. end
  157. end
  158. end

vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/contain_exactly.rb

32.31% lines covered

130 relevant lines. 42 lines covered and 88 lines missed.
    
  1. 1 module RSpec
  2. 1 module Matchers
  3. 1 module BuiltIn
  4. # rubocop:disable ClassLength
  5. # @api private
  6. # Provides the implementation for `contain_exactly` and `match_array`.
  7. # Not intended to be instantiated directly.
  8. 1 class ContainExactly < BaseMatcher
  9. # @api private
  10. # @return [String]
  11. 1 def failure_message
  12. if Array === actual
  13. generate_failure_message
  14. else
  15. "expected a collection that can be converted to an array with " \
  16. "`#to_ary` or `#to_a`, but got #{actual_formatted}"
  17. end
  18. end
  19. # @api private
  20. # @return [String]
  21. 1 def failure_message_when_negated
  22. list = EnglishPhrasing.list(surface_descriptions_in(expected))
  23. "expected #{actual_formatted} not to contain exactly#{list}"
  24. end
  25. # @api private
  26. # @return [String]
  27. 1 def description
  28. list = EnglishPhrasing.list(surface_descriptions_in(expected))
  29. "contain exactly#{list}"
  30. end
  31. 1 private
  32. 1 def generate_failure_message
  33. message = expected_collection_line
  34. message += actual_collection_line
  35. message += missing_elements_line unless missing_items.empty?
  36. message += extra_elements_line unless extra_items.empty?
  37. message
  38. end
  39. 1 def expected_collection_line
  40. message_line('expected collection contained', expected, true)
  41. end
  42. 1 def actual_collection_line
  43. message_line('actual collection contained', actual)
  44. end
  45. 1 def missing_elements_line
  46. message_line('the missing elements were', missing_items, true)
  47. end
  48. 1 def extra_elements_line
  49. message_line('the extra elements were', extra_items)
  50. end
  51. 1 def describe_collection(collection, surface_descriptions=false)
  52. if surface_descriptions
  53. "#{description_of(safe_sort(surface_descriptions_in collection))}\n"
  54. else
  55. "#{description_of(safe_sort(collection))}\n"
  56. end
  57. end
  58. 1 def message_line(prefix, collection, surface_descriptions=false)
  59. "%-32s%s" % [prefix + ':',
  60. describe_collection(collection, surface_descriptions)]
  61. end
  62. 1 def match(_expected, _actual)
  63. return false unless convert_actual_to_an_array
  64. match_when_sorted? || (extra_items.empty? && missing_items.empty?)
  65. end
  66. # This cannot always work (e.g. when dealing with unsortable items,
  67. # or matchers as expected items), but it's practically free compared to
  68. # the slowness of the full matching algorithm, and in common cases this
  69. # works, so it's worth a try.
  70. 1 def match_when_sorted?
  71. values_match?(safe_sort(expected), safe_sort(actual))
  72. end
  73. 1 def convert_actual_to_an_array
  74. if actual.respond_to?(:to_ary)
  75. @actual = actual.to_ary
  76. elsif actual.respond_to?(:to_a) && !to_a_disallowed?(actual)
  77. @actual = actual.to_a
  78. else
  79. false
  80. end
  81. end
  82. 1 def safe_sort(array)
  83. array.sort
  84. rescue Support::AllExceptionsExceptOnesWeMustNotRescue
  85. array
  86. end
  87. 1 if RUBY_VERSION == "1.8.7"
  88. def to_a_disallowed?(object)
  89. case object
  90. when NilClass, String then true
  91. else Kernel == RSpec::Support.method_handle_for(object, :to_a).owner
  92. end
  93. end
  94. else
  95. 1 def to_a_disallowed?(object)
  96. NilClass === object
  97. end
  98. end
  99. 1 def missing_items
  100. @missing_items ||= best_solution.unmatched_expected_indexes.map do |index|
  101. expected[index]
  102. end
  103. end
  104. 1 def extra_items
  105. @extra_items ||= best_solution.unmatched_actual_indexes.map do |index|
  106. actual[index]
  107. end
  108. end
  109. 1 def best_solution
  110. @best_solution ||= pairings_maximizer.find_best_solution
  111. end
  112. 1 def pairings_maximizer
  113. @pairings_maximizer ||= begin
  114. expected_matches = Hash[Array.new(expected.size) { |i| [i, []] }]
  115. actual_matches = Hash[Array.new(actual.size) { |i| [i, []] }]
  116. expected.each_with_index do |e, ei|
  117. actual.each_with_index do |a, ai|
  118. next unless values_match?(e, a)
  119. expected_matches[ei] << ai
  120. actual_matches[ai] << ei
  121. end
  122. end
  123. PairingsMaximizer.new(expected_matches, actual_matches)
  124. end
  125. end
  126. # Once we started supporting composing matchers, the algorithm for this matcher got
  127. # much more complicated. Consider this expression:
  128. #
  129. # expect(["fool", "food"]).to contain_exactly(/foo/, /fool/)
  130. #
  131. # This should pass (because we can pair /fool/ with "fool" and /foo/ with "food"), but
  132. # the original algorithm used by this matcher would pair the first elements it could
  133. # (/foo/ with "fool"), which would leave /fool/ and "food" unmatched. When we have
  134. # an expected element which is a matcher that matches a superset of actual items
  135. # compared to another expected element matcher, we need to consider every possible pairing.
  136. #
  137. # This class is designed to maximize the number of actual/expected pairings -- or,
  138. # conversely, to minimize the number of unpaired items. It's essentially a brute
  139. # force solution, but with a few heuristics applied to reduce the size of the
  140. # problem space:
  141. #
  142. # * Any items which match none of the items in the other list are immediately
  143. # placed into the `unmatched_expected_indexes` or `unmatched_actual_indexes` array.
  144. # The extra items and missing items in the matcher failure message are derived
  145. # from these arrays.
  146. # * Any items which reciprocally match only each other are paired up and not
  147. # considered further.
  148. #
  149. # What's left is only the items which match multiple items from the other list
  150. # (or vice versa). From here, it performs a brute-force depth-first search,
  151. # looking for a solution which pairs all elements in both lists, or, barring that,
  152. # that produces the fewest unmatched items.
  153. #
  154. # @private
  155. 1 class PairingsMaximizer
  156. # @private
  157. 1 Solution = Struct.new(:unmatched_expected_indexes, :unmatched_actual_indexes,
  158. :indeterminate_expected_indexes, :indeterminate_actual_indexes) do
  159. 1 def worse_than?(other)
  160. unmatched_item_count > other.unmatched_item_count
  161. end
  162. 1 def candidate?
  163. indeterminate_expected_indexes.empty? &&
  164. indeterminate_actual_indexes.empty?
  165. end
  166. 1 def ideal?
  167. candidate? && (
  168. unmatched_expected_indexes.empty? ||
  169. unmatched_actual_indexes.empty?
  170. )
  171. end
  172. 1 def unmatched_item_count
  173. unmatched_expected_indexes.count + unmatched_actual_indexes.count
  174. end
  175. 1 def +(derived_candidate_solution)
  176. self.class.new(
  177. unmatched_expected_indexes + derived_candidate_solution.unmatched_expected_indexes,
  178. unmatched_actual_indexes + derived_candidate_solution.unmatched_actual_indexes,
  179. # Ignore the indeterminate indexes: by the time we get here,
  180. # we've dealt with all indeterminates.
  181. [], []
  182. )
  183. end
  184. end
  185. 1 attr_reader :expected_to_actual_matched_indexes, :actual_to_expected_matched_indexes, :solution
  186. 1 def initialize(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
  187. @expected_to_actual_matched_indexes = expected_to_actual_matched_indexes
  188. @actual_to_expected_matched_indexes = actual_to_expected_matched_indexes
  189. unmatched_expected_indexes, indeterminate_expected_indexes =
  190. categorize_indexes(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
  191. unmatched_actual_indexes, indeterminate_actual_indexes =
  192. categorize_indexes(actual_to_expected_matched_indexes, expected_to_actual_matched_indexes)
  193. @solution = Solution.new(unmatched_expected_indexes, unmatched_actual_indexes,
  194. indeterminate_expected_indexes, indeterminate_actual_indexes)
  195. end
  196. 1 def find_best_solution
  197. return solution if solution.candidate?
  198. best_solution_so_far = NullSolution
  199. expected_index = solution.indeterminate_expected_indexes.first
  200. actuals = expected_to_actual_matched_indexes[expected_index]
  201. actuals.each do |actual_index|
  202. solution = best_solution_for_pairing(expected_index, actual_index)
  203. return solution if solution.ideal?
  204. best_solution_so_far = solution if best_solution_so_far.worse_than?(solution)
  205. end
  206. best_solution_so_far
  207. end
  208. 1 private
  209. # @private
  210. # Starting solution that is worse than any other real solution.
  211. 1 NullSolution = Class.new do
  212. 1 def self.worse_than?(_other)
  213. true
  214. end
  215. end
  216. 1 def categorize_indexes(indexes_to_categorize, other_indexes)
  217. unmatched = []
  218. indeterminate = []
  219. indexes_to_categorize.each_pair do |index, matches|
  220. if matches.empty?
  221. unmatched << index
  222. elsif !reciprocal_single_match?(matches, index, other_indexes)
  223. indeterminate << index
  224. end
  225. end
  226. return unmatched, indeterminate
  227. end
  228. 1 def reciprocal_single_match?(matches, index, other_list)
  229. return false unless matches.one?
  230. other_list[matches.first] == [index]
  231. end
  232. 1 def best_solution_for_pairing(expected_index, actual_index)
  233. modified_expecteds = apply_pairing_to(
  234. solution.indeterminate_expected_indexes,
  235. expected_to_actual_matched_indexes, actual_index)
  236. modified_expecteds.delete(expected_index)
  237. modified_actuals = apply_pairing_to(
  238. solution.indeterminate_actual_indexes,
  239. actual_to_expected_matched_indexes, expected_index)
  240. modified_actuals.delete(actual_index)
  241. solution + self.class.new(modified_expecteds, modified_actuals).find_best_solution
  242. end
  243. 1 def apply_pairing_to(indeterminates, original_matches, other_list_index)
  244. indeterminates.inject({}) do |accum, index|
  245. accum[index] = original_matches[index] - [other_list_index]
  246. accum
  247. end
  248. end
  249. end
  250. end
  251. # rubocop:enable ClassLength
  252. end
  253. end
  254. end

vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/eq.rb

86.67% lines covered

15 relevant lines. 13 lines covered and 2 lines missed.
    
  1. 1 module RSpec
  2. 1 module Matchers
  3. 1 module BuiltIn
  4. # @api private
  5. # Provides the implementation for `eq`.
  6. # Not intended to be instantiated directly.
  7. 1 class Eq < BaseMatcher
  8. # @api private
  9. # @return [String]
  10. 1 def failure_message
  11. 1 "\nexpected: #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
  12. end
  13. # @api private
  14. # @return [String]
  15. 1 def failure_message_when_negated
  16. "\nexpected: value != #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
  17. end
  18. # @api private
  19. # @return [String]
  20. 1 def description
  21. "eq #{expected_formatted}"
  22. end
  23. # @api private
  24. # @return [Boolean]
  25. 1 def diffable?
  26. 1 true
  27. end
  28. 1 private
  29. 1 def match(expected, actual)
  30. 10 actual == expected
  31. end
  32. end
  33. end
  34. end
  35. end

vender/bundle/ruby/2.6.0/gems/rspec-expectations-3.10.1/lib/rspec/matchers/built_in/operators.rb

52.54% lines covered

59 relevant lines. 31 lines covered and 28 lines missed.
    
  1. 1 require 'rspec/support'
  2. 1 module RSpec
  3. 1 module Matchers
  4. 1 module BuiltIn
  5. # @api private
  6. # Provides the implementation for operator matchers.
  7. # Not intended to be instantiated directly.
  8. # Only available for use with `should`.
  9. 1 class OperatorMatcher
  10. 1 class << self
  11. # @private
  12. 1 def registry
  13. 4 @registry ||= {}
  14. end
  15. # @private
  16. 1 def register(klass, operator, matcher)
  17. 2 registry[klass] ||= {}
  18. 2 registry[klass][operator] = matcher
  19. end
  20. # @private
  21. 1 def unregister(klass, operator)
  22. registry[klass] && registry[klass].delete(operator)
  23. end
  24. # @private
  25. 1 def get(klass, operator)
  26. klass.ancestors.each do |ancestor|
  27. matcher = registry[ancestor] && registry[ancestor][operator]
  28. return matcher if matcher
  29. end
  30. nil
  31. end
  32. end
  33. 1 register Enumerable, '=~', BuiltIn::ContainExactly
  34. 1 def initialize(actual)
  35. @actual = actual
  36. end
  37. # @private
  38. 1 def self.use_custom_matcher_or_delegate(operator)
  39. 7 define_method(operator) do |expected|
  40. if !has_non_generic_implementation_of?(operator) && (matcher = OperatorMatcher.get(@actual.class, operator))
  41. @actual.__send__(::RSpec::Matchers.last_expectation_handler.should_method, matcher.new(expected))
  42. else
  43. eval_match(@actual, operator, expected)
  44. end
  45. end
  46. 7 negative_operator = operator.sub(/^=/, '!')
  47. 7 if negative_operator != operator && respond_to?(negative_operator)
  48. 2 define_method(negative_operator) do |_expected|
  49. opposite_should = ::RSpec::Matchers.last_expectation_handler.opposite_should_method
  50. raise "RSpec does not support `#{::RSpec::Matchers.last_expectation_handler.should_method} #{negative_operator} expected`. " \
  51. "Use `#{opposite_should} #{operator} expected` instead."
  52. end
  53. end
  54. end
  55. 1 ['==', '===', '=~', '>', '>=', '<', '<='].each do |operator|
  56. 7 use_custom_matcher_or_delegate operator
  57. end
  58. # @private
  59. 1 def fail_with_message(message)
  60. RSpec::Expectations.fail_with(message, @expected, @actual)
  61. end
  62. # @api private
  63. # @return [String]
  64. 1 def description
  65. "#{@operator} #{RSpec::Support::ObjectFormatter.format(@expected)}"
  66. end
  67. 1 private
  68. 1 def has_non_generic_implementation_of?(op)
  69. Support.method_handle_for(@actual, op).owner != ::Kernel
  70. rescue NameError
  71. false
  72. end
  73. 1 def eval_match(actual, operator, expected)
  74. ::RSpec::Matchers.last_matcher = self
  75. @operator, @expected = operator, expected
  76. __delegate_operator(actual, operator, expected)
  77. end
  78. end
  79. # @private
  80. # Handles operator matcher for `should`.
  81. 1 class PositiveOperatorMatcher < OperatorMatcher
  82. 1 def __delegate_operator(actual, operator, expected)
  83. if actual.__send__(operator, expected)
  84. true
  85. else
  86. expected_formatted = RSpec::Support::ObjectFormatter.format(expected)
  87. actual_formatted = RSpec::Support::ObjectFormatter.format(actual)
  88. if ['==', '===', '=~'].include?(operator)
  89. fail_with_message("expected: #{expected_formatted}\n got: #{actual_formatted} (using #{operator})")
  90. else
  91. fail_with_message("expected: #{operator} #{expected_formatted}\n got: #{operator.gsub(/./, ' ')} #{actual_formatted}")
  92. end
  93. end
  94. end
  95. end
  96. # @private
  97. # Handles operator matcher for `should_not`.
  98. 1 class NegativeOperatorMatcher < OperatorMatcher
  99. 1 def __delegate_operator(actual, operator, expected)
  100. return false unless actual.__send__(operator, expected)
  101. expected_formatted = RSpec::Support::ObjectFormatter.format(expected)
  102. actual_formatted = RSpec::Support::ObjectFormatter.format(actual)
  103. fail_with_message("expected not: #{operator} #{expected_formatted}\n got: #{operator.gsub(/./, ' ')} #{actual_formatted}")
  104. end
  105. end
  106. end
  107. end
  108. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails.rb

100.0% lines covered

14 relevant lines. 14 lines covered and 0 lines missed.
    
  1. 1 require 'rspec/core'
  2. 1 require 'rails/version'
  3. # Load any of our adapters and extensions early in the process
  4. 1 require 'rspec/rails/adapters'
  5. 1 require 'rspec/rails/extensions'
  6. # Load the rspec-rails parts
  7. 1 require 'rspec/rails/view_rendering'
  8. 1 require 'rspec/rails/matchers'
  9. 1 require 'rspec/rails/fixture_support'
  10. 1 require 'rspec/rails/file_fixture_support'
  11. 1 require 'rspec/rails/fixture_file_upload_support'
  12. 1 require 'rspec/rails/example'
  13. 1 require 'rspec/rails/vendor/capybara'
  14. 1 require 'rspec/rails/configuration'
  15. 1 require 'rspec/rails/active_record'
  16. 1 require 'rspec/rails/feature_check'

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/active_record.rb

72.73% lines covered

11 relevant lines. 8 lines covered and 3 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # Fake class to document RSpec ActiveRecord configuration options. In practice,
  4. # these are dynamically added to the normal RSpec configuration object.
  5. 1 class ActiveRecordConfiguration
  6. # @private
  7. 1 def self.initialize_activerecord_configuration(config)
  8. 1 config.before :suite do
  9. # This allows dynamic columns etc to be used on ActiveRecord models when creating instance_doubles
  10. 1 if defined?(ActiveRecord) && defined?(ActiveRecord::Base) && defined?(::RSpec::Mocks) && (::RSpec::Mocks.respond_to?(:configuration))
  11. 1 ::RSpec::Mocks.configuration.when_declaring_verifying_double do |possible_model|
  12. target = possible_model.target
  13. if Class === target && ActiveRecord::Base > target && !target.abstract_class?
  14. target.define_attribute_methods
  15. end
  16. end
  17. end
  18. end
  19. end
  20. 1 initialize_activerecord_configuration RSpec.configuration
  21. end
  22. end
  23. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/adapters.rb

84.71% lines covered

85 relevant lines. 72 lines covered and 13 lines missed.
    
  1. 1 require 'delegate'
  2. 1 require 'active_support'
  3. 1 require 'active_support/concern'
  4. 1 require 'active_support/core_ext/string'
  5. 1 module RSpec
  6. 1 module Rails
  7. # @private
  8. 1 def self.disable_testunit_autorun
  9. # `Test::Unit::AutoRunner.need_auto_run=` was introduced to the test-unit
  10. # gem in version 2.4.9. Previous to this version `Test::Unit.run=` was
  11. # used. The implementation of test-unit included with Ruby has neither
  12. # method.
  13. if defined?(Test::Unit::AutoRunner.need_auto_run = ())
  14. Test::Unit::AutoRunner.need_auto_run = false
  15. elsif defined?(Test::Unit.run = ())
  16. Test::Unit.run = false
  17. end
  18. end
  19. 1 private_class_method :disable_testunit_autorun
  20. 1 if defined?(Kernel.gem)
  21. 1 gem 'minitest'
  22. else
  23. require 'minitest'
  24. end
  25. 1 require 'minitest/assertions'
  26. # Constant aliased to either Minitest or TestUnit, depending on what is
  27. # loaded.
  28. 1 Assertions = Minitest::Assertions
  29. # @private
  30. 1 class AssertionDelegator < Module
  31. 1 def initialize(*assertion_modules)
  32. 2 assertion_class = Class.new(SimpleDelegator) do
  33. 2 include ::RSpec::Rails::Assertions
  34. 2 include ::RSpec::Rails::MinitestCounters
  35. 4 assertion_modules.each { |mod| include mod }
  36. end
  37. 2 super() do
  38. 2 define_method :build_assertion_instance do
  39. assertion_class.new(self)
  40. end
  41. 2 def assertion_instance
  42. @assertion_instance ||= build_assertion_instance
  43. end
  44. 2 assertion_modules.each do |mod|
  45. 2 mod.public_instance_methods.each do |method|
  46. 12 next if method == :method_missing || method == "method_missing"
  47. 10 define_method(method.to_sym) do |*args, &block|
  48. assertion_instance.send(method.to_sym, *args, &block)
  49. end
  50. end
  51. end
  52. end
  53. end
  54. end
  55. # Adapts example groups for `Minitest::Test::LifecycleHooks`
  56. #
  57. # @private
  58. 1 module MinitestLifecycleAdapter
  59. 1 extend ActiveSupport::Concern
  60. 1 included do |group|
  61. 29 group.before { after_setup }
  62. 29 group.after { before_teardown }
  63. 13 group.around do |example|
  64. 16 before_setup
  65. 16 example.run
  66. 16 after_teardown
  67. end
  68. end
  69. 1 def before_setup
  70. end
  71. 1 def after_setup
  72. end
  73. 1 def before_teardown
  74. end
  75. 1 def after_teardown
  76. end
  77. end
  78. # @private
  79. 1 module MinitestCounters
  80. 1 attr_writer :assertions
  81. 1 def assertions
  82. @assertions ||= 0
  83. end
  84. end
  85. # @private
  86. 1 module SetupAndTeardownAdapter
  87. 1 extend ActiveSupport::Concern
  88. 1 module ClassMethods
  89. # Wraps `setup` calls from within Rails' testing framework in `before`
  90. # hooks.
  91. 1 def setup(*methods, &block)
  92. 4 methods.each do |method|
  93. 4 if method.to_s =~ /^setup_(with_controller|fixtures|controller_request_and_response)$/
  94. prepend_before { __send__ method }
  95. else
  96. 7 before { __send__ method }
  97. end
  98. end
  99. 4 before(&block) if block
  100. end
  101. # @api private
  102. #
  103. # Wraps `teardown` calls from within Rails' testing framework in
  104. # `after` hooks.
  105. 1 def teardown(*methods, &block)
  106. 11 methods.each { |method| after { __send__ method } }
  107. 4 after(&block) if block
  108. end
  109. end
  110. 1 def initialize(*args)
  111. 64 super
  112. 64 @example = nil
  113. end
  114. 1 def method_name
  115. @example
  116. end
  117. end
  118. # @private
  119. 1 module MinitestAssertionAdapter
  120. 1 extend ActiveSupport::Concern
  121. # @private
  122. 1 module ClassMethods
  123. # Returns the names of assertion methods that we want to expose to
  124. # examples without exposing non-assertion methods in Test::Unit or
  125. # Minitest.
  126. 1 def assertion_method_names
  127. 13 ::RSpec::Rails::Assertions
  128. .public_instance_methods
  129. .select do |m|
  130. 650 m.to_s =~ /^(assert|flunk|refute)/
  131. end
  132. end
  133. 1 def define_assertion_delegators
  134. 13 assertion_method_names.each do |m|
  135. 468 define_method(m.to_sym) do |*args, &block|
  136. assertion_delegator.send(m.to_sym, *args, &block)
  137. end
  138. end
  139. end
  140. end
  141. 1 class AssertionDelegator
  142. 1 include ::RSpec::Rails::Assertions
  143. 1 include ::RSpec::Rails::MinitestCounters
  144. end
  145. 1 def assertion_delegator
  146. @assertion_delegator ||= AssertionDelegator.new
  147. end
  148. 1 included do
  149. 13 define_assertion_delegators
  150. end
  151. end
  152. # Backwards compatibility. It's unlikely that anyone is using this
  153. # constant, but we had forgotten to mark it as `@private` earlier
  154. #
  155. # @private
  156. 1 TestUnitAssertionAdapter = MinitestAssertionAdapter
  157. end
  158. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/configuration.rb

83.54% lines covered

79 relevant lines. 66 lines covered and 13 lines missed.
    
  1. # rubocop: disable Metrics/ModuleLength
  2. 1 module RSpec
  3. 1 module Rails
  4. # Fake class to document RSpec Rails configuration options. In practice,
  5. # these are dynamically added to the normal RSpec configuration object.
  6. 1 class Configuration
  7. # @!method infer_spec_type_from_file_location!
  8. # Automatically tag specs in conventional directories with matching `type`
  9. # metadata so that they have relevant helpers available to them. See
  10. # `RSpec::Rails::DIRECTORY_MAPPINGS` for details on which metadata is
  11. # applied to each directory.
  12. # @!method render_views=(val)
  13. #
  14. # When set to `true`, controller specs will render the relevant view as
  15. # well. Defaults to `false`.
  16. # @!method render_views(val)
  17. # Enables view rendering for controllers specs.
  18. # @!method render_views?
  19. # Reader for currently value of `render_views` setting.
  20. end
  21. # Mappings used by `infer_spec_type_from_file_location!`.
  22. #
  23. # @api private
  24. DIRECTORY_MAPPINGS = {
  25. 1 channel: %w[spec channels],
  26. controller: %w[spec controllers],
  27. helper: %w[spec helpers],
  28. job: %w[spec jobs],
  29. mailer: %w[spec mailers],
  30. model: %w[spec models],
  31. request: %w[spec (requests|integration|api)],
  32. routing: %w[spec routing],
  33. view: %w[spec views],
  34. feature: %w[spec features],
  35. system: %w[spec system],
  36. mailbox: %w[spec mailboxes]
  37. }
  38. # Sets up the different example group modules for the different spec types
  39. #
  40. # @api private
  41. 1 def self.add_test_type_configurations(config)
  42. 1 config.include RSpec::Rails::ControllerExampleGroup, type: :controller
  43. 1 config.include RSpec::Rails::HelperExampleGroup, type: :helper
  44. 1 config.include RSpec::Rails::ModelExampleGroup, type: :model
  45. 1 config.include RSpec::Rails::RequestExampleGroup, type: :request
  46. 1 config.include RSpec::Rails::RoutingExampleGroup, type: :routing
  47. 1 config.include RSpec::Rails::ViewExampleGroup, type: :view
  48. 1 config.include RSpec::Rails::FeatureExampleGroup, type: :feature
  49. 1 config.include RSpec::Rails::Matchers
  50. 1 config.include RSpec::Rails::SystemExampleGroup, type: :system
  51. end
  52. # @private
  53. 1 def self.initialize_configuration(config) # rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity
  54. 1 config.backtrace_exclusion_patterns << /vendor\//
  55. 1 config.backtrace_exclusion_patterns << %r{lib/rspec/rails}
  56. # controller settings
  57. 1 config.add_setting :infer_base_class_for_anonymous_controllers, default: true
  58. # fixture support
  59. 1 config.add_setting :use_active_record, default: true
  60. 1 config.add_setting :use_transactional_fixtures, alias_with: :use_transactional_examples
  61. 1 config.add_setting :use_instantiated_fixtures
  62. 1 config.add_setting :global_fixtures
  63. 1 config.add_setting :fixture_path
  64. 1 config.include RSpec::Rails::FixtureSupport, :use_fixtures
  65. # We'll need to create a deprecated module in order to properly report to
  66. # gems / projects which are relying on this being loaded globally.
  67. #
  68. # See rspec/rspec-rails#1355 for history
  69. #
  70. # @deprecated Include `RSpec::Rails::RailsExampleGroup` or
  71. # `RSpec::Rails::FixtureSupport` directly instead
  72. 1 config.include RSpec::Rails::FixtureSupport
  73. 1 if ::Rails::VERSION::STRING > '5'
  74. 1 config.add_setting :file_fixture_path, default: 'spec/fixtures/files'
  75. 1 config.include RSpec::Rails::FileFixtureSupport
  76. end
  77. # Add support for fixture_path on fixture_file_upload
  78. 1 config.include RSpec::Rails::FixtureFileUploadSupport
  79. # This allows us to expose `render_views` as a config option even though it
  80. # breaks the convention of other options by using `render_views` as a
  81. # command (i.e. `render_views = true`), where it would normally be used
  82. # as a getter. This makes it easier for rspec-rails users because we use
  83. # `render_views` directly in example groups, so this aligns the two APIs,
  84. # but requires this workaround:
  85. 1 config.add_setting :rendering_views, default: false
  86. 1 config.instance_exec do
  87. 1 def render_views=(val)
  88. self.rendering_views = val
  89. end
  90. 1 def render_views
  91. self.rendering_views = true
  92. end
  93. 1 def render_views?
  94. rendering_views?
  95. end
  96. 1 undef :rendering_views? if respond_to?(:rendering_views?)
  97. 1 def rendering_views?
  98. !!rendering_views
  99. end
  100. # Define boolean predicates rather than relying on rspec-core due
  101. # to the bug fix in rspec/rspec-core#2736, note some of these
  102. # predicates are a bit nonsensical, but they exist for backwards
  103. # compatibility, we can tidy these up in `rspec-rails` 5.
  104. 1 undef :fixture_path? if respond_to?(:fixture_path?)
  105. 1 def fixture_path?
  106. !!fixture_path
  107. end
  108. 1 undef :global_fixtures? if respond_to?(:global_fixtures?)
  109. 1 def global_fixtures?
  110. !!global_fixtures
  111. end
  112. 1 undef :infer_base_class_for_anonymous_controllers? if respond_to?(:infer_base_class_for_anonymous_controllers?)
  113. 1 def infer_base_class_for_anonymous_controllers?
  114. !!infer_base_class_for_anonymous_controllers
  115. end
  116. 1 undef :use_instantiated_fixtures? if respond_to?(:use_instantiated_fixtures?)
  117. 1 def use_instantiated_fixtures?
  118. !!use_instantiated_fixtures
  119. end
  120. 1 undef :use_transactional_fixtures? if respond_to?(:use_transactional_fixtures?)
  121. 1 def use_transactional_fixtures?
  122. !!use_transactional_fixtures
  123. end
  124. 1 def infer_spec_type_from_file_location!
  125. 1 DIRECTORY_MAPPINGS.each do |type, dir_parts|
  126. 12 escaped_path = Regexp.compile(dir_parts.join('[\\\/]') + '[\\\/]')
  127. 12 define_derived_metadata(file_path: escaped_path) do |metadata|
  128. 51 metadata[:type] ||= type
  129. end
  130. end
  131. end
  132. # Adds exclusion filters for gems included with Rails
  133. 1 def filter_rails_from_backtrace!
  134. 1 filter_gems_from_backtrace "actionmailer", "actionpack", "actionview"
  135. 1 filter_gems_from_backtrace "activemodel", "activerecord",
  136. "activesupport", "activejob"
  137. end
  138. end
  139. 1 add_test_type_configurations(config)
  140. 1 if defined?(::Rails::Controller::Testing)
  141. [:controller, :view, :request].each do |type|
  142. config.include ::Rails::Controller::Testing::TestProcess, type: type
  143. config.include ::Rails::Controller::Testing::TemplateAssertions, type: type
  144. config.include ::Rails::Controller::Testing::Integration, type: type
  145. end
  146. end
  147. 1 if RSpec::Rails::FeatureCheck.has_action_mailer?
  148. 1 config.include RSpec::Rails::MailerExampleGroup, type: :mailer
  149. 17 config.after { ActionMailer::Base.deliveries.clear }
  150. end
  151. 1 if RSpec::Rails::FeatureCheck.has_active_job?
  152. 1 config.include RSpec::Rails::JobExampleGroup, type: :job
  153. end
  154. 1 if RSpec::Rails::FeatureCheck.has_action_cable_testing?
  155. 1 config.include RSpec::Rails::ChannelExampleGroup, type: :channel
  156. end
  157. 1 if RSpec::Rails::FeatureCheck.has_action_mailbox?
  158. 1 config.include RSpec::Rails::MailboxExampleGroup, type: :mailbox
  159. end
  160. end
  161. 1 initialize_configuration RSpec.configuration
  162. end
  163. end
  164. # rubocop: enable Metrics/ModuleLength

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example.rb

100.0% lines covered

13 relevant lines. 13 lines covered and 0 lines missed.
    
  1. 1 require 'rspec/rails/example/rails_example_group'
  2. 1 require 'rspec/rails/example/controller_example_group'
  3. 1 require 'rspec/rails/example/request_example_group'
  4. 1 require 'rspec/rails/example/helper_example_group'
  5. 1 require 'rspec/rails/example/view_example_group'
  6. 1 require 'rspec/rails/example/mailer_example_group'
  7. 1 require 'rspec/rails/example/routing_example_group'
  8. 1 require 'rspec/rails/example/model_example_group'
  9. 1 require 'rspec/rails/example/job_example_group'
  10. 1 require 'rspec/rails/example/feature_example_group'
  11. 1 require 'rspec/rails/example/system_example_group'
  12. 1 require 'rspec/rails/example/channel_example_group'
  13. 1 require 'rspec/rails/example/mailbox_example_group'

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/channel_example_group.rb

60.61% lines covered

33 relevant lines. 20 lines covered and 13 lines missed.
    
  1. 1 require "rspec/rails/matchers/action_cable/have_streams"
  2. 1 module RSpec
  3. 1 module Rails
  4. # @api public
  5. # Container module for channel spec functionality. It is only available if
  6. # ActionCable has been loaded before it.
  7. 1 module ChannelExampleGroup
  8. # @private
  9. 1 module ClassMethods
  10. # These blank modules are only necessary for YARD processing. It doesn't
  11. # handle the conditional check below very well and reports undocumented objects.
  12. end
  13. end
  14. end
  15. end
  16. 1 if RSpec::Rails::FeatureCheck.has_action_cable_testing?
  17. 1 module RSpec
  18. 1 module Rails
  19. # @api public
  20. # Container module for channel spec functionality.
  21. 1 module ChannelExampleGroup
  22. 1 extend ActiveSupport::Concern
  23. 1 include RSpec::Rails::RailsExampleGroup
  24. 1 include ActionCable::Connection::TestCase::Behavior
  25. 1 include ActionCable::Channel::TestCase::Behavior
  26. # Class-level DSL for channel specs.
  27. 1 module ClassMethods
  28. # @private
  29. 1 def channel_class
  30. (_channel_class || described_class).tap do |klass|
  31. next if klass <= ::ActionCable::Channel::Base
  32. raise "Described class is not a channel class.\n" \
  33. "Specify the channel class in the `describe` statement " \
  34. "or set it manually using `tests MyChannelClass`"
  35. end
  36. end
  37. # @private
  38. 1 def connection_class
  39. (_connection_class || described_class).tap do |klass|
  40. next if klass <= ::ActionCable::Connection::Base
  41. raise "Described class is not a connection class.\n" \
  42. "Specify the connection class in the `describe` statement " \
  43. "or set it manually using `tests MyConnectionClass`"
  44. end
  45. end
  46. end
  47. # Checks that the connection attempt has been rejected.
  48. #
  49. # @example
  50. # expect { connect }.to have_rejected_connection
  51. 1 def have_rejected_connection
  52. raise_error(::ActionCable::Connection::Authorization::UnauthorizedError)
  53. end
  54. # Checks that the subscription is subscribed to at least one stream.
  55. #
  56. # @example
  57. # expect(subscription).to have_streams
  58. 1 def have_streams
  59. check_subscribed!
  60. RSpec::Rails::Matchers::ActionCable::HaveStream.new
  61. end
  62. # Checks that the channel has been subscribed to the given stream
  63. #
  64. # @example
  65. # expect(subscription).to have_stream_from("chat_1")
  66. 1 def have_stream_from(stream)
  67. check_subscribed!
  68. RSpec::Rails::Matchers::ActionCable::HaveStream.new(stream)
  69. end
  70. # Checks that the channel has been subscribed to a stream for the given model
  71. #
  72. # @example
  73. # expect(subscription).to have_stream_for(user)
  74. 1 def have_stream_for(object)
  75. check_subscribed!
  76. RSpec::Rails::Matchers::ActionCable::HaveStream.new(broadcasting_for(object))
  77. end
  78. end
  79. end
  80. end
  81. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/controller_example_group.rb

34.62% lines covered

78 relevant lines. 27 lines covered and 51 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @private
  4. 1 ControllerAssertionDelegator = RSpec::Rails::AssertionDelegator.new(
  5. ActionDispatch::Assertions::RoutingAssertions
  6. )
  7. # @api public
  8. # Container module for controller spec functionality.
  9. 1 module ControllerExampleGroup
  10. 1 extend ActiveSupport::Concern
  11. 1 include RSpec::Rails::RailsExampleGroup
  12. 1 include ActionController::TestCase::Behavior
  13. 1 include RSpec::Rails::ViewRendering
  14. 1 include RSpec::Rails::Matchers::RedirectTo
  15. 1 include RSpec::Rails::Matchers::RenderTemplate
  16. 1 include RSpec::Rails::Matchers::RoutingMatchers
  17. 1 include ControllerAssertionDelegator
  18. # Class-level DSL for controller specs.
  19. 1 module ClassMethods
  20. # @private
  21. 1 def controller_class
  22. described_class
  23. end
  24. # Supports a simple DSL for specifying behavior of ApplicationController.
  25. # Creates an anonymous subclass of ApplicationController and evals the
  26. # `body` in that context. Also sets up implicit routes for this
  27. # controller, that are separate from those defined in "config/routes.rb".
  28. #
  29. # @note Due to Ruby 1.8 scoping rules in anonymous subclasses, constants
  30. # defined in `ApplicationController` must be fully qualified (e.g.
  31. # `ApplicationController::AccessDenied`) in the block passed to the
  32. # `controller` method. Any instance methods, filters, etc, that are
  33. # defined in `ApplicationController`, however, are accessible from
  34. # within the block.
  35. #
  36. # @example
  37. # describe ApplicationController do
  38. # controller do
  39. # def index
  40. # raise ApplicationController::AccessDenied
  41. # end
  42. # end
  43. #
  44. # describe "handling AccessDenied exceptions" do
  45. # it "redirects to the /401.html page" do
  46. # get :index
  47. # response.should redirect_to("/401.html")
  48. # end
  49. # end
  50. # end
  51. #
  52. # If you would like to spec a subclass of ApplicationController, call
  53. # controller like so:
  54. #
  55. # controller(ApplicationControllerSubclass) do
  56. # # ....
  57. # end
  58. 1 def controller(base_class = nil, &body)
  59. if RSpec.configuration.infer_base_class_for_anonymous_controllers?
  60. base_class ||= controller_class
  61. end
  62. base_class ||= defined?(ApplicationController) ? ApplicationController : ActionController::Base
  63. new_controller_class = Class.new(base_class) do
  64. def self.name
  65. root_controller = defined?(ApplicationController) ? ApplicationController : ActionController::Base
  66. if superclass == root_controller || superclass.abstract?
  67. "AnonymousController"
  68. else
  69. superclass.name
  70. end
  71. end
  72. end
  73. new_controller_class.class_exec(&body)
  74. (class << self; self; end).__send__(:define_method, :controller_class) { new_controller_class }
  75. before do
  76. @orig_routes = routes
  77. resource_name = if @controller.respond_to?(:controller_name)
  78. @controller.controller_name.to_sym
  79. else
  80. :anonymous
  81. end
  82. resource_path = if @controller.respond_to?(:controller_path)
  83. @controller.controller_path
  84. else
  85. resource_name.to_s
  86. end
  87. resource_module = resource_path.rpartition('/').first.presence
  88. resource_as = 'anonymous_' + resource_path.tr('/', '_')
  89. self.routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
  90. r.draw do
  91. resources resource_name,
  92. as: resource_as,
  93. module: resource_module,
  94. path: resource_path
  95. end
  96. end
  97. end
  98. after do
  99. self.routes = @orig_routes
  100. @orig_routes = nil
  101. end
  102. end
  103. # Specifies the routeset that will be used for the example group. This
  104. # is most useful when testing Rails engines.
  105. #
  106. # @example
  107. # describe MyEngine::PostsController do
  108. # routes { MyEngine::Engine.routes }
  109. #
  110. # # ...
  111. # end
  112. 1 def routes
  113. before do
  114. self.routes = yield
  115. end
  116. end
  117. end
  118. # @!attribute [r]
  119. # Returns the controller object instance under test.
  120. 1 attr_reader :controller
  121. # @!attribute [r]
  122. # Returns the Rails routes used for the spec.
  123. 1 attr_reader :routes
  124. # @private
  125. #
  126. # RSpec Rails uses this to make Rails routes easily available to specs.
  127. 1 def routes=(routes)
  128. @routes = routes
  129. assertion_instance.instance_variable_set(:@routes, routes)
  130. end
  131. # @private
  132. 1 module BypassRescue
  133. 1 def rescue_with_handler(exception)
  134. raise exception
  135. end
  136. end
  137. # Extends the controller with a module that overrides
  138. # `rescue_with_handler` to raise the exception passed to it. Use this to
  139. # specify that an action _should_ raise an exception given appropriate
  140. # conditions.
  141. #
  142. # @example
  143. # describe ProfilesController do
  144. # it "raises a 403 when a non-admin user tries to view another user's profile" do
  145. # profile = create_profile
  146. # login_as profile.user
  147. #
  148. # expect do
  149. # bypass_rescue
  150. # get :show, id: profile.id + 1
  151. # end.to raise_error(/403 Forbidden/)
  152. # end
  153. # end
  154. 1 def bypass_rescue
  155. controller.extend(BypassRescue)
  156. end
  157. # If method is a named_route, delegates to the RouteSet associated with
  158. # this controller.
  159. 1 def method_missing(method, *args, &block)
  160. if route_available?(method)
  161. controller.send(method, *args, &block)
  162. else
  163. super
  164. end
  165. end
  166. 1 included do
  167. subject { controller }
  168. before do
  169. self.routes = ::Rails.application.routes
  170. end
  171. around do |ex|
  172. previous_allow_forgery_protection_value = ActionController::Base.allow_forgery_protection
  173. begin
  174. ActionController::Base.allow_forgery_protection = false
  175. ex.call
  176. ensure
  177. ActionController::Base.allow_forgery_protection = previous_allow_forgery_protection_value
  178. end
  179. end
  180. end
  181. 1 private
  182. 1 def route_available?(method)
  183. (defined?(@routes) && route_defined?(routes, method)) ||
  184. (defined?(@orig_routes) && route_defined?(@orig_routes, method))
  185. end
  186. 1 def route_defined?(routes, method)
  187. return false if routes.nil?
  188. if routes.named_routes.respond_to?(:route_defined?)
  189. routes.named_routes.route_defined?(method)
  190. else
  191. routes.named_routes.helpers.include?(method)
  192. end
  193. end
  194. end
  195. end
  196. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/feature_example_group.rb

62.5% lines covered

24 relevant lines. 15 lines covered and 9 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @api public
  4. # Container module for routing spec functionality.
  5. 1 module FeatureExampleGroup
  6. 1 extend ActiveSupport::Concern
  7. 1 include RSpec::Rails::RailsExampleGroup
  8. # Default host to be used in Rails route helpers if none is specified.
  9. 1 DEFAULT_HOST = "www.example.com"
  10. 1 included do
  11. app = ::Rails.application
  12. if app.respond_to?(:routes)
  13. include app.routes.url_helpers if app.routes.respond_to?(:url_helpers)
  14. include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers)
  15. if respond_to?(:default_url_options)
  16. default_url_options[:host] ||= ::RSpec::Rails::FeatureExampleGroup::DEFAULT_HOST
  17. end
  18. end
  19. end
  20. # Shim to check for presence of Capybara. Will delegate if present, raise
  21. # if not. We assume here that in most cases `visit` will be the first
  22. # Capybara method called in a spec.
  23. 1 def visit(*)
  24. if defined?(super)
  25. super
  26. else
  27. raise "Capybara not loaded, please add it to your Gemfile:\n\ngem \"capybara\""
  28. end
  29. end
  30. end
  31. end
  32. end
  33. 1 unless RSpec.respond_to?(:feature)
  34. opts = {
  35. 1 capybara_feature: true,
  36. type: :feature,
  37. 1 skip: <<-EOT.squish
  38. Feature specs require the Capybara (https://github.com/teamcapybara/capybara)
  39. gem, version 2.13.0 or later.
  40. EOT
  41. }
  42. 1 RSpec.configure do |c|
  43. 1 c.alias_example_group_to :feature, opts
  44. 1 c.alias_example_to :scenario
  45. 1 c.alias_example_to :xscenario, skip: 'Temporarily skipped with xscenario'
  46. end
  47. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/helper_example_group.rb

66.67% lines covered

21 relevant lines. 14 lines covered and 7 lines missed.
    
  1. 1 require 'rspec/rails/view_assigns'
  2. 1 module RSpec
  3. 1 module Rails
  4. # @api public
  5. # Container module for helper specs.
  6. 1 module HelperExampleGroup
  7. 1 extend ActiveSupport::Concern
  8. 1 include RSpec::Rails::RailsExampleGroup
  9. 1 include ActionView::TestCase::Behavior
  10. 1 include RSpec::Rails::ViewAssigns
  11. # @private
  12. 1 module ClassMethods
  13. 1 def determine_constant_from_test_name(_ignore)
  14. described_class if yield(described_class)
  15. end
  16. end
  17. # Returns an instance of ActionView::Base with the helper being specified
  18. # mixed in, along with any of the built-in rails helpers.
  19. 1 def helper
  20. _view.tap do |v|
  21. v.extend(ApplicationHelper) if defined?(ApplicationHelper)
  22. v.assign(view_assigns)
  23. end
  24. end
  25. 1 private
  26. 1 def _controller_path(example)
  27. example.example_group.described_class.to_s.sub(/Helper/, '').underscore
  28. end
  29. 1 included do
  30. before do |example|
  31. controller.controller_path = _controller_path(example)
  32. end
  33. end
  34. end
  35. end
  36. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/job_example_group.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @api public
  4. # Container module for job spec functionality. It is only available if
  5. # ActiveJob has been loaded before it.
  6. 1 module JobExampleGroup
  7. # This blank module is only necessary for YARD processing. It doesn't
  8. # handle the conditional `defined?` check below very well.
  9. end
  10. end
  11. end
  12. 1 if defined?(ActiveJob)
  13. 1 module RSpec
  14. 1 module Rails
  15. # Container module for job spec functionality.
  16. 1 module JobExampleGroup
  17. 1 extend ActiveSupport::Concern
  18. 1 include RSpec::Rails::RailsExampleGroup
  19. end
  20. end
  21. end
  22. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/mailbox_example_group.rb

55.56% lines covered

27 relevant lines. 15 lines covered and 12 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @api public
  4. # Container module for mailbox spec functionality.
  5. 1 module MailboxExampleGroup
  6. 1 extend ActiveSupport::Concern
  7. 1 if RSpec::Rails::FeatureCheck.has_action_mailbox?
  8. 1 require 'action_mailbox/test_helper'
  9. 1 extend ::ActionMailbox::TestHelper
  10. # @private
  11. 1 def self.create_inbound_email(arg)
  12. case arg
  13. when Hash
  14. create_inbound_email_from_mail(**arg)
  15. else
  16. create_inbound_email_from_source(arg.to_s)
  17. end
  18. end
  19. else
  20. def self.create_inbound_email(_arg)
  21. raise "Could not load ActionMailer::TestHelper"
  22. end
  23. end
  24. 1 class_methods do
  25. # @private
  26. 1 def mailbox_class
  27. described_class
  28. end
  29. end
  30. 1 included do
  31. subject { described_class }
  32. end
  33. # @api public
  34. # Passes if the inbound email was delivered
  35. #
  36. # @example
  37. # inbound_email = process(args)
  38. # expect(inbound_email).to have_been_delivered
  39. 1 def have_been_delivered
  40. satisfy('have been delivered', &:delivered?)
  41. end
  42. # @api public
  43. # Passes if the inbound email bounced during processing
  44. #
  45. # @example
  46. # inbound_email = process(args)
  47. # expect(inbound_email).to have_bounced
  48. 1 def have_bounced
  49. satisfy('have bounced', &:bounced?)
  50. end
  51. # @api public
  52. # Passes if the inbound email failed to process
  53. #
  54. # @example
  55. # inbound_email = process(args)
  56. # expect(inbound_email).to have_failed
  57. 1 def have_failed
  58. satisfy('have failed', &:failed?)
  59. end
  60. # Process an inbound email message directly, bypassing routing.
  61. #
  62. # @param message [Hash, Mail::Message] a mail message or hash of
  63. # attributes used to build one
  64. # @return [ActionMaibox::InboundMessage]
  65. 1 def process(message)
  66. MailboxExampleGroup.create_inbound_email(message).tap do |mail|
  67. self.class.mailbox_class.receive(mail)
  68. end
  69. end
  70. end
  71. end
  72. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/mailer_example_group.rb

76.47% lines covered

17 relevant lines. 13 lines covered and 4 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @api public
  4. # Container module for mailer spec functionality. It is only available if
  5. # ActionMailer has been loaded before it.
  6. 1 module MailerExampleGroup
  7. # This blank module is only necessary for YARD processing. It doesn't
  8. # handle the conditional `defined?` check below very well.
  9. end
  10. end
  11. end
  12. 1 if defined?(ActionMailer)
  13. 1 module RSpec
  14. 1 module Rails
  15. # Container module for mailer spec functionality.
  16. 1 module MailerExampleGroup
  17. 1 extend ActiveSupport::Concern
  18. 1 include RSpec::Rails::RailsExampleGroup
  19. 1 include ActionMailer::TestCase::Behavior
  20. 1 included do
  21. include ::Rails.application.routes.url_helpers
  22. options = ::Rails.configuration.action_mailer.default_url_options || {}
  23. options.each { |key, value| default_url_options[key] = value }
  24. end
  25. # Class-level DSL for mailer specs.
  26. 1 module ClassMethods
  27. # Alias for `described_class`.
  28. 1 def mailer_class
  29. described_class
  30. end
  31. end
  32. end
  33. end
  34. end
  35. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/model_example_group.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @api public
  4. # Container class for model spec functionality. Does not provide anything
  5. # special over the common RailsExampleGroup currently.
  6. 1 module ModelExampleGroup
  7. 1 extend ActiveSupport::Concern
  8. 1 include RSpec::Rails::RailsExampleGroup
  9. end
  10. end
  11. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/rails_example_group.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. # Temporary workaround to resolve circular dependency between rspec-rails' spec
  2. # suite and ammeter.
  3. 1 require 'rspec/rails/matchers'
  4. 1 module RSpec
  5. 1 module Rails
  6. # @api public
  7. # Common rails example functionality.
  8. 1 module RailsExampleGroup
  9. 1 extend ActiveSupport::Concern
  10. 1 include RSpec::Rails::SetupAndTeardownAdapter
  11. 1 include RSpec::Rails::MinitestLifecycleAdapter
  12. 1 include RSpec::Rails::MinitestAssertionAdapter
  13. 1 include RSpec::Rails::FixtureSupport
  14. end
  15. end
  16. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/request_example_group.rb

100.0% lines covered

17 relevant lines. 17 lines covered and 0 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @api public
  4. # Container class for request spec functionality.
  5. 1 module RequestExampleGroup
  6. 1 extend ActiveSupport::Concern
  7. 1 include RSpec::Rails::RailsExampleGroup
  8. 1 include ActionDispatch::Integration::Runner
  9. 1 include ActionDispatch::Assertions
  10. 1 include RSpec::Rails::Matchers::RedirectTo
  11. 1 include RSpec::Rails::Matchers::RenderTemplate
  12. 1 include ActionController::TemplateAssertions
  13. 1 if ActionPack::VERSION::MAJOR >= 5
  14. 1 include ActionDispatch::IntegrationTest::Behavior
  15. end
  16. # Delegates to `Rails.application`.
  17. 1 def app
  18. 2 ::Rails.application
  19. end
  20. 1 included do
  21. 4 before do
  22. 3 @routes = ::Rails.application.routes
  23. end
  24. end
  25. end
  26. end
  27. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/routing_example_group.rb

70.83% lines covered

24 relevant lines. 17 lines covered and 7 lines missed.
    
  1. 1 require "action_dispatch/testing/assertions/routing"
  2. 1 module RSpec
  3. 1 module Rails
  4. # @private
  5. 1 RoutingAssertionDelegator = RSpec::Rails::AssertionDelegator.new(
  6. ActionDispatch::Assertions::RoutingAssertions
  7. )
  8. # @api public
  9. # Container module for routing spec functionality.
  10. 1 module RoutingExampleGroup
  11. 1 extend ActiveSupport::Concern
  12. 1 include RSpec::Rails::RailsExampleGroup
  13. 1 include RSpec::Rails::Matchers::RoutingMatchers
  14. 1 include RSpec::Rails::Matchers::RoutingMatchers::RouteHelpers
  15. 1 include RSpec::Rails::RoutingAssertionDelegator
  16. # Class-level DSL for route specs.
  17. 1 module ClassMethods
  18. # Specifies the routeset that will be used for the example group. This
  19. # is most useful when testing Rails engines.
  20. #
  21. # @example
  22. # describe MyEngine::PostsController do
  23. # routes { MyEngine::Engine.routes }
  24. #
  25. # it "routes posts#index" do
  26. # expect(:get => "/posts").to
  27. # route_to(:controller => "my_engine/posts", :action => "index")
  28. # end
  29. # end
  30. 1 def routes
  31. before do
  32. self.routes = yield
  33. end
  34. end
  35. end
  36. 1 included do
  37. before do
  38. self.routes = ::Rails.application.routes
  39. end
  40. end
  41. # @!attribute [r]
  42. # @private
  43. 1 attr_reader :routes
  44. # @private
  45. 1 def routes=(routes)
  46. @routes = routes
  47. assertion_instance.instance_variable_set(:@routes, routes)
  48. end
  49. 1 private
  50. 1 def method_missing(m, *args, &block)
  51. routes.url_helpers.respond_to?(m) ? routes.url_helpers.send(m, *args) : super
  52. end
  53. end
  54. end
  55. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/system_example_group.rb

31.03% lines covered

58 relevant lines. 18 lines covered and 40 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @api public
  4. # Container class for system tests
  5. 1 module SystemExampleGroup
  6. 1 extend ActiveSupport::Concern
  7. 1 include RSpec::Rails::RailsExampleGroup
  8. 1 include RSpec::Rails::Matchers::RedirectTo
  9. 1 include RSpec::Rails::Matchers::RenderTemplate
  10. 1 include ActionDispatch::Integration::Runner
  11. 1 include ActionDispatch::Assertions
  12. 1 include ActionController::TemplateAssertions
  13. # Special characters to translate into underscores for #method_name
  14. 1 CHARS_TO_TRANSLATE = ['/', '.', ':', ',', "'", '"', " "].freeze
  15. # @private
  16. 1 module BlowAwayTeardownHooks
  17. # @private
  18. 1 def before_teardown
  19. end
  20. # @private
  21. 1 def after_teardown
  22. end
  23. end
  24. # for the SystemTesting Screenshot situation
  25. 1 def passed?
  26. return false if RSpec.current_example.exception
  27. return true unless defined?(::RSpec::Expectations::FailureAggregator)
  28. failure_notifier = ::RSpec::Support.failure_notifier
  29. return true unless failure_notifier.is_a?(::RSpec::Expectations::FailureAggregator)
  30. failure_notifier.failures.empty? && failure_notifier.other_errors.empty?
  31. end
  32. # @private
  33. 1 def method_name
  34. @method_name ||= [
  35. self.class.name.underscore,
  36. RSpec.current_example.description.underscore
  37. ].join("_").tr(CHARS_TO_TRANSLATE.join, "_")[0...200] + "_#{rand(1000)}"
  38. end
  39. # Delegates to `Rails.application`.
  40. 1 def app
  41. ::Rails.application
  42. end
  43. 1 included do |other|
  44. ActiveSupport.on_load(:action_dispatch_system_test_case) do
  45. ActionDispatch::SystemTesting::Server.silence_puma = true
  46. end
  47. begin
  48. require 'capybara'
  49. require 'action_dispatch/system_test_case'
  50. rescue LoadError => e
  51. abort """
  52. LoadError: #{e.message}
  53. System test integration requires Rails >= 5.1 and has a hard
  54. dependency on a webserver and `capybara`, please add capybara to
  55. your Gemfile and configure a webserver (e.g. `Capybara.server =
  56. :webrick`) before attempting to use system specs.
  57. """.gsub(/\s+/, ' ').strip
  58. end
  59. if ::Rails::VERSION::STRING >= '6.0'
  60. original_before_teardown =
  61. ::ActionDispatch::SystemTesting::TestHelpers::SetupAndTeardown.instance_method(:before_teardown)
  62. end
  63. original_after_teardown =
  64. ::ActionDispatch::SystemTesting::TestHelpers::SetupAndTeardown.instance_method(:after_teardown)
  65. other.include ::ActionDispatch::SystemTesting::TestHelpers::SetupAndTeardown
  66. other.include ::ActionDispatch::SystemTesting::TestHelpers::ScreenshotHelper
  67. other.include BlowAwayTeardownHooks
  68. attr_reader :driver
  69. if ActionDispatch::SystemTesting::Server.respond_to?(:silence_puma=)
  70. ActionDispatch::SystemTesting::Server.silence_puma = true
  71. end
  72. def initialize(*args, &blk)
  73. super(*args, &blk)
  74. @driver = nil
  75. self.class.before do
  76. # A user may have already set the driver, so only default if driver
  77. # is not set
  78. driven_by(:selenium) unless @driver
  79. end
  80. end
  81. def driven_by(driver, **driver_options, &blk)
  82. @driver = ::ActionDispatch::SystemTestCase.driven_by(driver, **driver_options, &blk).tap(&:use)
  83. end
  84. before do
  85. @routes = ::Rails.application.routes
  86. end
  87. after do
  88. orig_stdout = $stdout
  89. $stdout = StringIO.new
  90. begin
  91. if ::Rails::VERSION::STRING >= '6.0'
  92. original_before_teardown.bind(self).call
  93. end
  94. original_after_teardown.bind(self).call
  95. ensure
  96. myio = $stdout
  97. myio.rewind
  98. RSpec.current_example.metadata[:extra_failure_lines] = myio.readlines
  99. $stdout = orig_stdout
  100. end
  101. end
  102. end
  103. end
  104. end
  105. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/example/view_example_group.rb

35.16% lines covered

91 relevant lines. 32 lines covered and 59 lines missed.
    
  1. 1 require 'rspec/rails/view_assigns'
  2. 1 require 'rspec/rails/view_spec_methods'
  3. 1 require 'rspec/rails/view_path_builder'
  4. 1 module RSpec
  5. 1 module Rails
  6. # @api public
  7. # Container class for view spec functionality.
  8. 1 module ViewExampleGroup
  9. 1 extend ActiveSupport::Concern
  10. 1 include RSpec::Rails::RailsExampleGroup
  11. 1 include ActionView::TestCase::Behavior
  12. 1 include RSpec::Rails::ViewAssigns
  13. 1 include RSpec::Rails::Matchers::RenderTemplate
  14. # @private
  15. 1 module StubResolverCache
  16. 1 def self.resolver_for(hash)
  17. @resolvers ||= {}
  18. @resolvers[hash] ||= ActionView::FixtureResolver.new(hash)
  19. end
  20. end
  21. # @private
  22. 1 module ClassMethods
  23. 1 def _default_helper
  24. base = metadata[:description].split('/')[0..-2].join('/')
  25. (base.camelize + 'Helper').constantize unless base.to_s.empty?
  26. rescue NameError
  27. nil
  28. end
  29. 1 def _default_helpers
  30. helpers = [_default_helper].compact
  31. helpers << ApplicationHelper if Object.const_defined?('ApplicationHelper')
  32. helpers
  33. end
  34. end
  35. # DSL exposed to view specs.
  36. 1 module ExampleMethods
  37. 1 extend ActiveSupport::Concern
  38. 1 included do
  39. include ::Rails.application.routes.url_helpers
  40. include ::Rails.application.routes.mounted_helpers
  41. end
  42. # @overload render
  43. # @overload render({partial: path_to_file})
  44. # @overload render({partial: path_to_file}, {... locals ...})
  45. # @overload render({partial: path_to_file}, {... locals ...}) do ... end
  46. #
  47. # Delegates to ActionView::Base#render, so see documentation on that
  48. # for more info.
  49. #
  50. # The only addition is that you can call render with no arguments, and
  51. # RSpec will pass the top level description to render:
  52. #
  53. # describe "widgets/new.html.erb" do
  54. # it "shows all the widgets" do
  55. # render # => view.render(file: "widgets/new.html.erb")
  56. # # ...
  57. # end
  58. # end
  59. 1 def render(options = {}, local_assigns = {}, &block)
  60. options = _default_render_options if Hash === options && options.empty?
  61. super(options, local_assigns, &block)
  62. end
  63. # The instance of `ActionView::Base` that is used to render the template.
  64. # Use this to stub methods _before_ calling `render`.
  65. #
  66. # describe "widgets/new.html.erb" do
  67. # it "shows all the widgets" do
  68. # view.stub(:foo) { "foo" }
  69. # render
  70. # # ...
  71. # end
  72. # end
  73. 1 def view
  74. _view
  75. end
  76. # Simulates the presence of a template on the file system by adding a
  77. # Rails' FixtureResolver to the front of the view_paths list. Designed to
  78. # help isolate view examples from partials rendered by the view template
  79. # that is the subject of the example.
  80. #
  81. # stub_template("widgets/_widget.html.erb" => "This content.")
  82. 1 def stub_template(hash)
  83. view.view_paths.unshift(StubResolverCache.resolver_for(hash))
  84. end
  85. # Provides access to the params hash that will be available within the
  86. # view.
  87. #
  88. # params[:foo] = 'bar'
  89. 1 def params
  90. controller.params
  91. end
  92. # @deprecated Use `view` instead.
  93. 1 def template
  94. RSpec.deprecate("template", replacement: "view")
  95. view
  96. end
  97. # @deprecated Use `rendered` instead.
  98. 1 def response
  99. # `assert_template` expects `response` to implement a #body method
  100. # like an `ActionDispatch::Response` does to force the view to
  101. # render. For backwards compatibility, we use #response as an alias
  102. # for #rendered, but it needs to implement #body to avoid
  103. # `assert_template` raising a `NoMethodError`.
  104. unless rendered.respond_to?(:body)
  105. def rendered.body
  106. self
  107. end
  108. end
  109. rendered
  110. end
  111. 1 private
  112. 1 def _default_render_options
  113. formats = if ActionView::Template::Types.respond_to?(:symbols)
  114. ActionView::Template::Types.symbols
  115. else
  116. [:html, :text, :js, :css, :xml, :json].map(&:to_s)
  117. end.map { |x| Regexp.escape(x) }.join("|")
  118. handlers = ActionView::Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
  119. locales = "[a-z]{2}(?:-[A-Z]{2})?"
  120. variants = "[^.]*"
  121. path_regex = %r{
  122. \A
  123. (?<template>.*?)
  124. (?:\.(?<locale>#{locales}))??
  125. (?:\.(?<format>#{formats}))??
  126. (?:\+(?<variant>#{variants}))??
  127. (?:\.(?<handler>#{handlers}))?
  128. \z
  129. }x
  130. # This regex should always find a match.
  131. # Worst case, everything will be nil, and :template will just be
  132. # the original string.
  133. match = path_regex.match(_default_file_to_render)
  134. render_options = {template: match[:template]}
  135. render_options[:handlers] = [match[:handler]] if match[:handler]
  136. render_options[:formats] = [match[:format].to_sym] if match[:format]
  137. render_options[:locales] = [match[:locale]] if match[:locale]
  138. render_options[:variants] = [match[:variant]] if match[:variant]
  139. render_options
  140. end
  141. 1 def _path_parts
  142. _default_file_to_render.split("/")
  143. end
  144. 1 def _controller_path
  145. _path_parts[0..-2].join("/")
  146. end
  147. 1 def _inferred_action
  148. _path_parts.last.split(".").first
  149. end
  150. 1 def _include_controller_helpers
  151. helpers = controller._helpers
  152. view.singleton_class.class_exec do
  153. include helpers unless included_modules.include?(helpers)
  154. end
  155. end
  156. end
  157. 1 included do
  158. include ExampleMethods
  159. helper(*_default_helpers)
  160. before do
  161. _include_controller_helpers
  162. view.lookup_context.prefixes << _controller_path
  163. controller.controller_path = _controller_path
  164. path_params_to_merge = {}
  165. path_params_to_merge[:controller] = _controller_path
  166. path_params_to_merge[:action] = _inferred_action unless _inferred_action =~ /^_/
  167. path_params = controller.request.path_parameters
  168. controller.request.path_parameters = path_params.reverse_merge(path_params_to_merge)
  169. controller.request.path = ViewPathBuilder.new(::Rails.application.routes).path_for(controller.request.path_parameters)
  170. ViewSpecMethods.add_to(::ActionView::TestCase::TestController)
  171. end
  172. after do
  173. ViewSpecMethods.remove_from(::ActionView::TestCase::TestController)
  174. end
  175. let(:_default_file_to_render) do |example|
  176. example.example_group.top_level_description
  177. end
  178. end
  179. end
  180. end
  181. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/extensions.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 require 'rspec/rails/extensions/active_record/proxy'

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/extensions/active_record/proxy.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. 1 RSpec.configure do |rspec|
  2. # Delay this in order to give users a chance to configure `expect_with`...
  3. 1 rspec.before(:suite) do
  4. 1 if defined?(RSpec::Matchers) &&
  5. RSpec::Matchers.configuration.respond_to?(:syntax) && # RSpec 4 dropped support for monkey-patching `should` syntax
  6. RSpec::Matchers.configuration.syntax.include?(:should) &&
  7. defined?(ActiveRecord::Associations)
  8. 1 RSpec::Matchers.configuration.add_should_and_should_not_to ActiveRecord::Associations::CollectionProxy
  9. end
  10. end
  11. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/file_fixture_support.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. 1 if ::Rails::VERSION::STRING > '5'
  2. 1 require 'active_support/testing/file_fixtures'
  3. 1 module RSpec
  4. 1 module Rails
  5. # @private
  6. 1 module FileFixtureSupport
  7. 1 extend ActiveSupport::Concern
  8. 1 include ActiveSupport::Testing::FileFixtures
  9. 1 included do
  10. 13 self.file_fixture_path = RSpec.configuration.file_fixture_path
  11. end
  12. end
  13. end
  14. end
  15. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/fixture_file_upload_support.rb

61.11% lines covered

18 relevant lines. 11 lines covered and 7 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @private
  4. 1 module FixtureFileUploadSupport
  5. 1 delegate :fixture_file_upload, to: :rails_fixture_file_wrapper
  6. 1 private
  7. 1 def rails_fixture_file_wrapper
  8. RailsFixtureFileWrapper.fixture_path = nil
  9. resolved_fixture_path =
  10. if respond_to?(:fixture_path) && !fixture_path.nil?
  11. fixture_path.to_s
  12. else
  13. (RSpec.configuration.fixture_path || '').to_s
  14. end
  15. RailsFixtureFileWrapper.fixture_path = File.join(resolved_fixture_path, '') unless resolved_fixture_path.strip.empty?
  16. RailsFixtureFileWrapper.instance
  17. end
  18. 1 class RailsFixtureFileWrapper
  19. 1 include ActionDispatch::TestProcess if defined?(ActionDispatch::TestProcess)
  20. 1 class << self
  21. 1 attr_accessor :fixture_path
  22. # Get instance of wrapper
  23. 1 def instance
  24. @instance ||= new
  25. end
  26. end
  27. end
  28. end
  29. end
  30. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/fixture_support.rb

69.44% lines covered

36 relevant lines. 25 lines covered and 11 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # @private
  4. 1 module FixtureSupport
  5. 1 if defined?(ActiveRecord::TestFixtures)
  6. 1 extend ActiveSupport::Concern
  7. 1 include RSpec::Rails::SetupAndTeardownAdapter
  8. 1 include RSpec::Rails::MinitestLifecycleAdapter
  9. 1 include RSpec::Rails::MinitestAssertionAdapter
  10. 1 include ActiveRecord::TestFixtures
  11. 1 included do
  12. 13 if RSpec.configuration.use_active_record?
  13. 13 include Fixtures
  14. 13 self.fixture_path = RSpec.configuration.fixture_path
  15. 13 if ::Rails::VERSION::STRING > '5'
  16. 13 self.use_transactional_tests = RSpec.configuration.use_transactional_fixtures
  17. else
  18. self.use_transactional_fixtures = RSpec.configuration.use_transactional_fixtures
  19. end
  20. 13 self.use_instantiated_fixtures = RSpec.configuration.use_instantiated_fixtures
  21. 13 fixtures RSpec.configuration.global_fixtures if RSpec.configuration.global_fixtures
  22. end
  23. end
  24. 1 module Fixtures
  25. 1 extend ActiveSupport::Concern
  26. 1 class_methods do
  27. 1 def fixtures(*args)
  28. orig_methods = private_instance_methods
  29. super.tap do
  30. new_methods = private_instance_methods - orig_methods
  31. new_methods.each do |method_name|
  32. proxy_method_warning_if_called_in_before_context_scope(method_name)
  33. end
  34. end
  35. end
  36. 1 def proxy_method_warning_if_called_in_before_context_scope(method_name)
  37. orig_implementation = instance_method(method_name)
  38. define_method(method_name) do |*args, &blk|
  39. if inspect.include?("before(:context)")
  40. RSpec.warn_with("Calling fixture method in before :context ")
  41. else
  42. orig_implementation.bind(self).call(*args, &blk)
  43. end
  44. end
  45. end
  46. end
  47. 1 if ::Rails.version.to_f >= 6.1
  48. # @private return the example name for TestFixtures
  49. 1 def name
  50. 32 @example
  51. end
  52. end
  53. end
  54. end
  55. end
  56. end
  57. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers.rb

100.0% lines covered

22 relevant lines. 22 lines covered and 0 lines missed.
    
  1. 1 require 'rspec/core/warnings'
  2. 1 require 'rspec/expectations'
  3. 1 require 'rspec/rails/feature_check'
  4. 1 module RSpec
  5. 1 module Rails
  6. # @api public
  7. # Container module for Rails specific matchers.
  8. 1 module Matchers
  9. end
  10. end
  11. end
  12. 1 require 'rspec/rails/matchers/base_matcher'
  13. 1 require 'rspec/rails/matchers/have_rendered'
  14. 1 require 'rspec/rails/matchers/redirect_to'
  15. 1 require 'rspec/rails/matchers/routing_matchers'
  16. 1 require 'rspec/rails/matchers/be_new_record'
  17. 1 require 'rspec/rails/matchers/be_a_new'
  18. 1 require 'rspec/rails/matchers/relation_match_array'
  19. 1 require 'rspec/rails/matchers/be_valid'
  20. 1 require 'rspec/rails/matchers/have_http_status'
  21. 1 if RSpec::Rails::FeatureCheck.has_active_job?
  22. 1 require 'rspec/rails/matchers/active_job'
  23. 1 require 'rspec/rails/matchers/have_enqueued_mail'
  24. end
  25. 1 if RSpec::Rails::FeatureCheck.has_action_cable_testing?
  26. 1 require 'rspec/rails/matchers/action_cable'
  27. end
  28. 1 if RSpec::Rails::FeatureCheck.has_action_mailbox?
  29. 1 require 'rspec/rails/matchers/action_mailbox'
  30. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable.rb

69.23% lines covered

13 relevant lines. 9 lines covered and 4 lines missed.
    
  1. 1 require "rspec/rails/matchers/action_cable/have_broadcasted_to"
  2. 1 module RSpec
  3. 1 module Rails
  4. 1 module Matchers
  5. # Namespace for various implementations of ActionCable features
  6. #
  7. # @api private
  8. 1 module ActionCable
  9. end
  10. # @api public
  11. # Passes if a message has been sent to a stream/object inside a block.
  12. # May chain `at_least`, `at_most` or `exactly` to specify a number of times.
  13. # To specify channel from which message has been broadcasted to object use `from_channel`.
  14. #
  15. #
  16. # @example
  17. # expect {
  18. # ActionCable.server.broadcast "messages", text: 'Hi!'
  19. # }.to have_broadcasted_to("messages")
  20. #
  21. # expect {
  22. # SomeChannel.broadcast_to(user)
  23. # }.to have_broadcasted_to(user).from_channel(SomeChannel)
  24. #
  25. # # Using alias
  26. # expect {
  27. # ActionCable.server.broadcast "messages", text: 'Hi!'
  28. # }.to broadcast_to("messages")
  29. #
  30. # expect {
  31. # ActionCable.server.broadcast "messages", text: 'Hi!'
  32. # ActionCable.server.broadcast "all", text: 'Hi!'
  33. # }.to have_broadcasted_to("messages").exactly(:once)
  34. #
  35. # expect {
  36. # 3.times { ActionCable.server.broadcast "messages", text: 'Hi!' }
  37. # }.to have_broadcasted_to("messages").at_least(2).times
  38. #
  39. # expect {
  40. # ActionCable.server.broadcast "messages", text: 'Hi!'
  41. # }.to have_broadcasted_to("messages").at_most(:twice)
  42. #
  43. # expect {
  44. # ActionCable.server.broadcast "messages", text: 'Hi!'
  45. # }.to have_broadcasted_to("messages").with(text: 'Hi!')
  46. 1 def have_broadcasted_to(target = nil)
  47. check_action_cable_adapter
  48. ActionCable::HaveBroadcastedTo.new(target, channel: described_class)
  49. end
  50. 1 alias_method :broadcast_to, :have_broadcasted_to
  51. 1 private
  52. # @private
  53. 1 def check_action_cable_adapter
  54. return if ::ActionCable::SubscriptionAdapter::Test === ::ActionCable.server.pubsub
  55. raise StandardError, "To use ActionCable matchers set `adapter: test` in your cable.yml"
  56. end
  57. end
  58. end
  59. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb

28.87% lines covered

97 relevant lines. 28 lines covered and 69 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. 1 module ActionCable
  5. # rubocop: disable Metrics/ClassLength
  6. # @private
  7. 1 class HaveBroadcastedTo < RSpec::Matchers::BuiltIn::BaseMatcher
  8. 1 def initialize(target, channel:)
  9. @target = target
  10. @channel = channel
  11. @block = proc { }
  12. @data = nil
  13. set_expected_number(:exactly, 1)
  14. end
  15. 1 def with(data = nil, &block)
  16. @data = data
  17. @data = @data.with_indifferent_access if @data.is_a?(Hash)
  18. @block = block if block
  19. self
  20. end
  21. 1 def exactly(count)
  22. set_expected_number(:exactly, count)
  23. self
  24. end
  25. 1 def at_least(count)
  26. set_expected_number(:at_least, count)
  27. self
  28. end
  29. 1 def at_most(count)
  30. set_expected_number(:at_most, count)
  31. self
  32. end
  33. 1 def times
  34. self
  35. end
  36. 1 def once
  37. exactly(:once)
  38. end
  39. 1 def twice
  40. exactly(:twice)
  41. end
  42. 1 def thrice
  43. exactly(:thrice)
  44. end
  45. 1 def failure_message
  46. "expected to broadcast #{base_message}".tap do |msg|
  47. if @unmatching_msgs.any?
  48. msg << "\nBroadcasted messages to #{stream}:"
  49. @unmatching_msgs.each do |data|
  50. msg << "\n #{data}"
  51. end
  52. end
  53. end
  54. end
  55. 1 def failure_message_when_negated
  56. "expected not to broadcast #{base_message}"
  57. end
  58. 1 def message_expectation_modifier
  59. case @expectation_type
  60. when :exactly then "exactly"
  61. when :at_most then "at most"
  62. when :at_least then "at least"
  63. end
  64. end
  65. 1 def supports_block_expectations?
  66. true
  67. end
  68. 1 def matches?(proc)
  69. raise ArgumentError, "have_broadcasted_to and broadcast_to only support block expectations" unless Proc === proc
  70. original_sent_messages_count = pubsub_adapter.broadcasts(stream).size
  71. proc.call
  72. in_block_messages = pubsub_adapter.broadcasts(stream).drop(original_sent_messages_count)
  73. check(in_block_messages)
  74. end
  75. 1 def from_channel(channel)
  76. @channel = channel
  77. self
  78. end
  79. 1 private
  80. 1 def stream
  81. @stream ||= if @target.is_a?(String)
  82. @target
  83. else
  84. check_channel_presence
  85. @channel.broadcasting_for(@target)
  86. end
  87. end
  88. 1 def check(messages)
  89. @matching_msgs, @unmatching_msgs = messages.partition do |msg|
  90. decoded = ActiveSupport::JSON.decode(msg)
  91. decoded = decoded.with_indifferent_access if decoded.is_a?(Hash)
  92. if @data.nil? || @data === decoded
  93. @block.call(decoded)
  94. true
  95. else
  96. false
  97. end
  98. end
  99. @matching_msgs_count = @matching_msgs.size
  100. case @expectation_type
  101. when :exactly then @expected_number == @matching_msgs_count
  102. when :at_most then @expected_number >= @matching_msgs_count
  103. when :at_least then @expected_number <= @matching_msgs_count
  104. end
  105. end
  106. 1 def set_expected_number(relativity, count)
  107. @expectation_type = relativity
  108. @expected_number =
  109. case count
  110. when :once then 1
  111. when :twice then 2
  112. when :thrice then 3
  113. else Integer(count)
  114. end
  115. end
  116. 1 def base_message
  117. "#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg|
  118. msg << " with #{data_description(@data)}" unless @data.nil?
  119. msg << ", but broadcast #{@matching_msgs_count}"
  120. end
  121. end
  122. 1 def data_description(data)
  123. if data.is_a?(RSpec::Matchers::Composable)
  124. data.description
  125. else
  126. data
  127. end
  128. end
  129. 1 def pubsub_adapter
  130. ::ActionCable.server.pubsub
  131. end
  132. 1 def check_channel_presence
  133. return if @channel.present? && @channel.respond_to?(:channel_name)
  134. error_msg = "Broadcasting channel can't be infered. Please, specify it with `from_channel`"
  135. raise ArgumentError, error_msg
  136. end
  137. end
  138. # rubocop: enable Metrics/ClassLength
  139. end
  140. end
  141. end
  142. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_cable/have_streams.rb

54.17% lines covered

24 relevant lines. 13 lines covered and 11 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. 1 module ActionCable
  5. # @api private
  6. # Provides the implementation for `have_stream`, `have_stream_for`, and `have_stream_from`.
  7. # Not intended to be instantiated directly.
  8. 1 class HaveStream < RSpec::Matchers::BuiltIn::BaseMatcher
  9. # @api private
  10. # @return [String]
  11. 1 def failure_message
  12. "expected to have #{base_message}"
  13. end
  14. # @api private
  15. # @return [String]
  16. 1 def failure_message_when_negated
  17. "expected not to have #{base_message}"
  18. end
  19. # @api private
  20. # @return [Boolean]
  21. 1 def matches?(subscription)
  22. raise(ArgumentError, "have_streams is used for negated expectations only") if no_expected?
  23. match(subscription)
  24. end
  25. # @api private
  26. # @return [Boolean]
  27. 1 def does_not_match?(subscription)
  28. !match(subscription)
  29. end
  30. 1 private
  31. 1 def match(subscription)
  32. case subscription
  33. when ::ActionCable::Channel::Base
  34. @actual = subscription.streams
  35. no_expected? ? actual.any? : actual.any? { |i| expected === i }
  36. else
  37. raise ArgumentError, "have_stream, have_stream_from and have_stream_from support expectations on subscription only"
  38. end
  39. end
  40. 1 def base_message
  41. no_expected? ? "any stream started" : "stream #{expected_formatted} started, but have #{actual_formatted}"
  42. end
  43. 1 def no_expected?
  44. !defined?(@expected)
  45. end
  46. end
  47. end
  48. end
  49. end
  50. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/action_mailbox.rb

57.14% lines covered

28 relevant lines. 16 lines covered and 12 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. # Namespace for various implementations of ActionMailbox features
  5. #
  6. # @api private
  7. 1 module ActionMailbox
  8. # @private
  9. 1 class Base < RSpec::Rails::Matchers::BaseMatcher
  10. 1 private
  11. 1 def create_inbound_email(message)
  12. RSpec::Rails::MailboxExampleGroup.create_inbound_email(message)
  13. end
  14. end
  15. # @private
  16. 1 class ReceiveInboundEmail < Base
  17. 1 def initialize(message)
  18. super()
  19. @inbound_email = create_inbound_email(message)
  20. end
  21. 1 def matches?(mailbox)
  22. @mailbox = mailbox
  23. @receiver = ApplicationMailbox.router.send(:match_to_mailbox, inbound_email)
  24. @receiver == @mailbox
  25. end
  26. 1 def failure_message
  27. "expected #{describe_inbound_email} to route to #{mailbox}".tap do |msg|
  28. if receiver
  29. msg << ", but routed to #{receiver} instead"
  30. end
  31. end
  32. end
  33. 1 def failure_message_when_negated
  34. "expected #{describe_inbound_email} not to route to #{mailbox}"
  35. end
  36. 1 private
  37. 1 attr_reader :inbound_email, :mailbox, :receiver
  38. 1 def describe_inbound_email
  39. "mail to #{inbound_email.mail.to.to_sentence}"
  40. end
  41. end
  42. end
  43. # @api public
  44. # Passes if the given inbound email would be routed to the subject inbox.
  45. #
  46. # @param message [Hash, Mail::Message] a mail message or hash of
  47. # attributes used to build one
  48. 1 def receive_inbound_email(message)
  49. ActionMailbox::ReceiveInboundEmail.new(message)
  50. end
  51. end
  52. end
  53. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/active_job.rb

35.43% lines covered

175 relevant lines. 62 lines covered and 113 lines missed.
    
  1. 1 require "active_job/base"
  2. 1 require "active_job/arguments"
  3. 1 module RSpec
  4. 1 module Rails
  5. 1 module Matchers
  6. # Namespace for various implementations of ActiveJob features
  7. #
  8. # @api private
  9. 1 module ActiveJob
  10. # rubocop: disable Metrics/ClassLength
  11. # @private
  12. 1 class Base < RSpec::Rails::Matchers::BaseMatcher
  13. 1 def initialize
  14. @args = []
  15. @queue = nil
  16. @at = nil
  17. @block = proc { }
  18. set_expected_number(:exactly, 1)
  19. end
  20. 1 def with(*args, &block)
  21. @args = args
  22. @block = block if block.present?
  23. self
  24. end
  25. 1 def on_queue(queue)
  26. @queue = queue.to_s
  27. self
  28. end
  29. 1 def at(time_or_date)
  30. case time_or_date
  31. when Time then @at = Time.at(time_or_date.to_f)
  32. else
  33. @at = time_or_date
  34. end
  35. self
  36. end
  37. 1 def exactly(count)
  38. set_expected_number(:exactly, count)
  39. self
  40. end
  41. 1 def at_least(count)
  42. set_expected_number(:at_least, count)
  43. self
  44. end
  45. 1 def at_most(count)
  46. set_expected_number(:at_most, count)
  47. self
  48. end
  49. 1 def times
  50. self
  51. end
  52. 1 def once
  53. exactly(:once)
  54. end
  55. 1 def twice
  56. exactly(:twice)
  57. end
  58. 1 def thrice
  59. exactly(:thrice)
  60. end
  61. 1 def failure_message
  62. "expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
  63. if @unmatching_jobs.any?
  64. msg << "\nQueued jobs:"
  65. @unmatching_jobs.each do |job|
  66. msg << "\n #{base_job_message(job)}"
  67. end
  68. end
  69. end
  70. end
  71. 1 def failure_message_when_negated
  72. "expected not to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}"
  73. end
  74. 1 def message_expectation_modifier
  75. case @expectation_type
  76. when :exactly then "exactly"
  77. when :at_most then "at most"
  78. when :at_least then "at least"
  79. end
  80. end
  81. 1 def supports_block_expectations?
  82. true
  83. end
  84. 1 private
  85. 1 def check(jobs)
  86. @matching_jobs, @unmatching_jobs = jobs.partition do |job|
  87. if job_match?(job) && arguments_match?(job) && queue_match?(job) && at_match?(job)
  88. args = deserialize_arguments(job)
  89. @block.call(*args)
  90. true
  91. else
  92. false
  93. end
  94. end
  95. @matching_jobs_count = @matching_jobs.size
  96. case @expectation_type
  97. when :exactly then @expected_number == @matching_jobs_count
  98. when :at_most then @expected_number >= @matching_jobs_count
  99. when :at_least then @expected_number <= @matching_jobs_count
  100. end
  101. end
  102. 1 def base_message
  103. "#{message_expectation_modifier} #{@expected_number} jobs,".tap do |msg|
  104. msg << " with #{@args}," if @args.any?
  105. msg << " on queue #{@queue}," if @queue
  106. msg << " at #{@at.inspect}," if @at
  107. msg << " but #{self.class::MESSAGE_EXPECTATION_ACTION} #{@matching_jobs_count}"
  108. end
  109. end
  110. 1 def base_job_message(job)
  111. msg_parts = []
  112. msg_parts << "with #{deserialize_arguments(job)}" if job[:args].any?
  113. msg_parts << "on queue #{job[:queue]}" if job[:queue]
  114. msg_parts << "at #{Time.at(job[:at])}" if job[:at]
  115. "#{job[:job].name} job".tap do |msg|
  116. msg << " #{msg_parts.join(', ')}" if msg_parts.any?
  117. end
  118. end
  119. 1 def job_match?(job)
  120. @job ? @job == job[:job] : true
  121. end
  122. 1 def arguments_match?(job)
  123. if @args.any?
  124. args = serialize_and_deserialize_arguments(@args)
  125. deserialized_args = deserialize_arguments(job)
  126. RSpec::Mocks::ArgumentListMatcher.new(*args).args_match?(*deserialized_args)
  127. else
  128. true
  129. end
  130. end
  131. 1 def queue_match?(job)
  132. return true unless @queue
  133. @queue == job[:queue]
  134. end
  135. 1 def at_match?(job)
  136. return true unless @at
  137. return job[:at].nil? if @at == :no_wait
  138. return false unless job[:at]
  139. values_match?(@at, Time.at(job[:at]))
  140. end
  141. 1 def set_expected_number(relativity, count)
  142. @expectation_type = relativity
  143. @expected_number = case count
  144. when :once then 1
  145. when :twice then 2
  146. when :thrice then 3
  147. else Integer(count)
  148. end
  149. end
  150. 1 def serialize_and_deserialize_arguments(args)
  151. serialized = ::ActiveJob::Arguments.serialize(args)
  152. ::ActiveJob::Arguments.deserialize(serialized)
  153. rescue ::ActiveJob::SerializationError
  154. args
  155. end
  156. 1 def deserialize_arguments(job)
  157. ::ActiveJob::Arguments.deserialize(job[:args])
  158. rescue ::ActiveJob::DeserializationError
  159. job[:args]
  160. end
  161. 1 def queue_adapter
  162. ::ActiveJob::Base.queue_adapter
  163. end
  164. end
  165. # rubocop: enable Metrics/ClassLength
  166. # @private
  167. 1 class HaveEnqueuedJob < Base
  168. 1 FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
  169. 1 MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
  170. 1 def initialize(job)
  171. super()
  172. @job = job
  173. end
  174. 1 def matches?(proc)
  175. raise ArgumentError, "have_enqueued_job and enqueue_job only support block expectations" unless Proc === proc
  176. original_enqueued_jobs_count = queue_adapter.enqueued_jobs.count
  177. proc.call
  178. in_block_jobs = queue_adapter.enqueued_jobs.drop(original_enqueued_jobs_count)
  179. check(in_block_jobs)
  180. end
  181. 1 def does_not_match?(proc)
  182. set_expected_number(:at_least, 1)
  183. !matches?(proc)
  184. end
  185. end
  186. # @private
  187. 1 class HaveBeenEnqueued < Base
  188. 1 FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
  189. 1 MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
  190. 1 def matches?(job)
  191. @job = job
  192. check(queue_adapter.enqueued_jobs)
  193. end
  194. 1 def does_not_match?(proc)
  195. set_expected_number(:at_least, 1)
  196. !matches?(proc)
  197. end
  198. end
  199. # @private
  200. 1 class HavePerformedJob < Base
  201. 1 FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
  202. 1 MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
  203. 1 def initialize(job)
  204. super()
  205. @job = job
  206. end
  207. 1 def matches?(proc)
  208. raise ArgumentError, "have_performed_job only supports block expectations" unless Proc === proc
  209. original_performed_jobs_count = queue_adapter.performed_jobs.count
  210. proc.call
  211. in_block_jobs = queue_adapter.performed_jobs.drop(original_performed_jobs_count)
  212. check(in_block_jobs)
  213. end
  214. end
  215. # @private
  216. 1 class HaveBeenPerformed < Base
  217. 1 FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
  218. 1 MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
  219. 1 def matches?(job)
  220. @job = job
  221. check(queue_adapter.performed_jobs)
  222. end
  223. end
  224. end
  225. # @api public
  226. # Passes if a job has been enqueued inside block. May chain at_least, at_most or exactly to specify a number of times.
  227. #
  228. # @example
  229. # expect {
  230. # HeavyLiftingJob.perform_later
  231. # }.to have_enqueued_job
  232. #
  233. # # Using alias
  234. # expect {
  235. # HeavyLiftingJob.perform_later
  236. # }.to enqueue_job
  237. #
  238. # expect {
  239. # HelloJob.perform_later
  240. # HeavyLiftingJob.perform_later
  241. # }.to have_enqueued_job(HelloJob).exactly(:once)
  242. #
  243. # expect {
  244. # 3.times { HelloJob.perform_later }
  245. # }.to have_enqueued_job(HelloJob).at_least(2).times
  246. #
  247. # expect {
  248. # HelloJob.perform_later
  249. # }.to have_enqueued_job(HelloJob).at_most(:twice)
  250. #
  251. # expect {
  252. # HelloJob.perform_later
  253. # HeavyLiftingJob.perform_later
  254. # }.to have_enqueued_job(HelloJob).and have_enqueued_job(HeavyLiftingJob)
  255. #
  256. # expect {
  257. # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
  258. # }.to have_enqueued_job.with(42).on_queue("low").at(Date.tomorrow.noon)
  259. #
  260. # expect {
  261. # HelloJob.set(queue: "low").perform_later(42)
  262. # }.to have_enqueued_job.with(42).on_queue("low").at(:no_wait)
  263. #
  264. # expect {
  265. # HelloJob.perform_later('rspec_rails', 'rails', 42)
  266. # }.to have_enqueued_job.with { |from, to, times|
  267. # # Perform more complex argument matching using dynamic arguments
  268. # expect(from).to include "_#{to}"
  269. # }
  270. 1 def have_enqueued_job(job = nil)
  271. check_active_job_adapter
  272. ActiveJob::HaveEnqueuedJob.new(job)
  273. end
  274. 1 alias_method :enqueue_job, :have_enqueued_job
  275. # @api public
  276. # Passes if a job has been enqueued. May chain at_least, at_most or exactly to specify a number of times.
  277. #
  278. # @example
  279. # before { ActiveJob::Base.queue_adapter.enqueued_jobs.clear }
  280. #
  281. # HeavyLiftingJob.perform_later
  282. # expect(HeavyLiftingJob).to have_been_enqueued
  283. #
  284. # HelloJob.perform_later
  285. # HeavyLiftingJob.perform_later
  286. # expect(HeavyLiftingJob).to have_been_enqueued.exactly(:once)
  287. #
  288. # 3.times { HelloJob.perform_later }
  289. # expect(HelloJob).to have_been_enqueued.at_least(2).times
  290. #
  291. # HelloJob.perform_later
  292. # expect(HelloJob).to enqueue_job(HelloJob).at_most(:twice)
  293. #
  294. # HelloJob.perform_later
  295. # HeavyLiftingJob.perform_later
  296. # expect(HelloJob).to have_been_enqueued
  297. # expect(HeavyLiftingJob).to have_been_enqueued
  298. #
  299. # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
  300. # expect(HelloJob).to have_been_enqueued.with(42).on_queue("low").at(Date.tomorrow.noon)
  301. #
  302. # HelloJob.set(queue: "low").perform_later(42)
  303. # expect(HelloJob).to have_been_enqueued.with(42).on_queue("low").at(:no_wait)
  304. 1 def have_been_enqueued
  305. check_active_job_adapter
  306. ActiveJob::HaveBeenEnqueued.new
  307. end
  308. # @api public
  309. # Passes if a job has been performed inside block. May chain at_least, at_most or exactly to specify a number of times.
  310. #
  311. # @example
  312. # expect {
  313. # perform_jobs { HeavyLiftingJob.perform_later }
  314. # }.to have_performed_job
  315. #
  316. # expect {
  317. # perform_jobs {
  318. # HelloJob.perform_later
  319. # HeavyLiftingJob.perform_later
  320. # }
  321. # }.to have_performed_job(HelloJob).exactly(:once)
  322. #
  323. # expect {
  324. # perform_jobs { 3.times { HelloJob.perform_later } }
  325. # }.to have_performed_job(HelloJob).at_least(2).times
  326. #
  327. # expect {
  328. # perform_jobs { HelloJob.perform_later }
  329. # }.to have_performed_job(HelloJob).at_most(:twice)
  330. #
  331. # expect {
  332. # perform_jobs {
  333. # HelloJob.perform_later
  334. # HeavyLiftingJob.perform_later
  335. # }
  336. # }.to have_performed_job(HelloJob).and have_performed_job(HeavyLiftingJob)
  337. #
  338. # expect {
  339. # perform_jobs {
  340. # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
  341. # }
  342. # }.to have_performed_job.with(42).on_queue("low").at(Date.tomorrow.noon)
  343. 1 def have_performed_job(job = nil)
  344. check_active_job_adapter
  345. ActiveJob::HavePerformedJob.new(job)
  346. end
  347. 1 alias_method :perform_job, :have_performed_job
  348. # @api public
  349. # Passes if a job has been performed. May chain at_least, at_most or exactly to specify a number of times.
  350. #
  351. # @example
  352. # before do
  353. # ActiveJob::Base.queue_adapter.performed_jobs.clear
  354. # ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
  355. # ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
  356. # end
  357. #
  358. # HeavyLiftingJob.perform_later
  359. # expect(HeavyLiftingJob).to have_been_performed
  360. #
  361. # HelloJob.perform_later
  362. # HeavyLiftingJob.perform_later
  363. # expect(HeavyLiftingJob).to have_been_performed.exactly(:once)
  364. #
  365. # 3.times { HelloJob.perform_later }
  366. # expect(HelloJob).to have_been_performed.at_least(2).times
  367. #
  368. # HelloJob.perform_later
  369. # HeavyLiftingJob.perform_later
  370. # expect(HelloJob).to have_been_performed
  371. # expect(HeavyLiftingJob).to have_been_performed
  372. #
  373. # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
  374. # expect(HelloJob).to have_been_performed.with(42).on_queue("low").at(Date.tomorrow.noon)
  375. 1 def have_been_performed
  376. check_active_job_adapter
  377. ActiveJob::HaveBeenPerformed.new
  378. end
  379. 1 private
  380. # @private
  381. 1 def check_active_job_adapter
  382. return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
  383. raise StandardError, "To use ActiveJob matchers set `ActiveJob::Base.queue_adapter = :test`"
  384. end
  385. end
  386. end
  387. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/base_matcher.rb

49.25% lines covered

67 relevant lines. 33 lines covered and 34 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. # @api private
  5. #
  6. # Base class to build matchers. Should not be instantiated directly.
  7. 1 class BaseMatcher
  8. 1 include RSpec::Matchers::Composable
  9. # @api private
  10. # Used to detect when no arg is passed to `initialize`.
  11. # `nil` cannot be used because it's a valid value to pass.
  12. 1 UNDEFINED = Object.new.freeze
  13. # @private
  14. 1 attr_reader :actual, :expected, :rescued_exception
  15. # @private
  16. 1 attr_writer :matcher_name
  17. 1 def initialize(expected = UNDEFINED)
  18. @expected = expected unless UNDEFINED.equal?(expected)
  19. end
  20. # @api private
  21. # Indicates if the match is successful. Delegates to `match`, which
  22. # should be defined on a subclass. Takes care of consistently
  23. # initializing the `actual` attribute.
  24. 1 def matches?(actual)
  25. @actual = actual
  26. match(expected, actual)
  27. end
  28. # @api private
  29. # Used to wrap a block of code that will indicate failure by
  30. # raising one of the named exceptions.
  31. #
  32. # This is used by rspec-rails for some of its matchers that
  33. # wrap rails' assertions.
  34. 1 def match_unless_raises(*exceptions)
  35. exceptions.unshift Exception if exceptions.empty?
  36. begin
  37. yield
  38. true
  39. rescue *exceptions => @rescued_exception
  40. false
  41. end
  42. end
  43. # @api private
  44. # Generates a description using {RSpec::Matchers::EnglishPhrasing}.
  45. # @return [String]
  46. 1 def description
  47. desc = RSpec::Matchers::EnglishPhrasing.split_words(self.class.matcher_name)
  48. desc << RSpec::Matchers::EnglishPhrasing.list(@expected) if defined?(@expected)
  49. desc
  50. end
  51. # @api private
  52. # Matchers are not diffable by default. Override this to make your
  53. # subclass diffable.
  54. 1 def diffable?
  55. false
  56. end
  57. # @api private
  58. # Most matchers are value matchers (i.e. meant to work with `expect(value)`)
  59. # rather than block matchers (i.e. meant to work with `expect { }`), so
  60. # this defaults to false. Block matchers must override this to return true.
  61. 1 def supports_block_expectations?
  62. false
  63. end
  64. # @api private
  65. 1 def expects_call_stack_jump?
  66. false
  67. end
  68. # @private
  69. 1 def expected_formatted
  70. RSpec::Support::ObjectFormatter.format(@expected)
  71. end
  72. # @private
  73. 1 def actual_formatted
  74. RSpec::Support::ObjectFormatter.format(@actual)
  75. end
  76. # @private
  77. 1 def self.matcher_name
  78. @matcher_name ||= underscore(name.split('::').last)
  79. end
  80. # @private
  81. 1 def matcher_name
  82. if defined?(@matcher_name)
  83. @matcher_name
  84. else
  85. self.class.matcher_name
  86. end
  87. end
  88. # @private
  89. # Borrowed from ActiveSupport.
  90. 1 def self.underscore(camel_cased_word)
  91. word = camel_cased_word.to_s.dup
  92. word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
  93. word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
  94. word.tr!('-', '_')
  95. word.downcase!
  96. word
  97. end
  98. 1 private_class_method :underscore
  99. 1 private
  100. 1 def assert_ivars(*expected_ivars)
  101. return unless (expected_ivars - present_ivars).any?
  102. ivar_list = RSpec::Matchers::EnglishPhrasing.list(expected_ivars)
  103. raise "#{self.class.name} needs to supply#{ivar_list}"
  104. end
  105. 1 alias present_ivars instance_variables
  106. # @private
  107. 1 module HashFormatting
  108. # `{ :a => 5, :b => 2 }.inspect` produces:
  109. #
  110. # {:a=>5, :b=>2}
  111. #
  112. # ...but it looks much better as:
  113. #
  114. # {:a => 5, :b => 2}
  115. #
  116. # This is idempotent and safe to run on a string multiple times.
  117. 1 def improve_hash_formatting(inspect_string)
  118. inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
  119. end
  120. 1 module_function :improve_hash_formatting
  121. end
  122. 1 include HashFormatting
  123. # @api private
  124. # Provides default implementations of failure messages, based on the `description`.
  125. 1 module DefaultFailureMessages
  126. # @api private
  127. # Provides a good generic failure message. Based on `description`.
  128. # When subclassing, if you are not satisfied with this failure message
  129. # you often only need to override `description`.
  130. # @return [String]
  131. 1 def failure_message
  132. "expected #{description_of @actual} to #{description}".dup
  133. end
  134. # @api private
  135. # Provides a good generic negative failure message. Based on `description`.
  136. # When subclassing, if you are not satisfied with this failure message
  137. # you often only need to override `description`.
  138. # @return [String]
  139. 1 def failure_message_when_negated
  140. "expected #{description_of @actual} not to #{description}".dup
  141. end
  142. # @private
  143. 1 def self.has_default_failure_messages?(matcher)
  144. matcher.method(:failure_message).owner == self &&
  145. matcher.method(:failure_message_when_negated).owner == self
  146. rescue NameError
  147. false
  148. end
  149. end
  150. 1 include DefaultFailureMessages
  151. end
  152. end
  153. end
  154. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_a_new.rb

40.63% lines covered

32 relevant lines. 13 lines covered and 19 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. # @api private
  5. #
  6. # Matcher class for `be_a_new`. Should not be instantiated directly.
  7. #
  8. # @see RSpec::Rails::Matchers#be_a_new
  9. 1 class BeANew < RSpec::Rails::Matchers::BaseMatcher
  10. # @private
  11. 1 def initialize(expected)
  12. @expected = expected
  13. end
  14. # @private
  15. 1 def matches?(actual)
  16. @actual = actual
  17. actual.is_a?(expected) && actual.new_record? && attributes_match?(actual)
  18. end
  19. # @api public
  20. # @see RSpec::Rails::Matchers#be_a_new
  21. 1 def with(expected_attributes)
  22. attributes.merge!(expected_attributes)
  23. self
  24. end
  25. # @private
  26. 1 def failure_message
  27. [].tap do |message|
  28. unless actual.is_a?(expected) && actual.new_record?
  29. message << "expected #{actual.inspect} to be a new #{expected.inspect}"
  30. end
  31. unless attributes_match?(actual)
  32. describe_unmatched_attributes = surface_descriptions_in(unmatched_attributes)
  33. if unmatched_attributes.size > 1
  34. message << "attributes #{describe_unmatched_attributes.inspect} were not set on #{actual.inspect}"
  35. else
  36. message << "attribute #{describe_unmatched_attributes.inspect} was not set on #{actual.inspect}"
  37. end
  38. end
  39. end.join(' and ')
  40. end
  41. 1 private
  42. 1 def attributes
  43. @attributes ||= {}
  44. end
  45. 1 def attributes_match?(actual)
  46. attributes.stringify_keys.all? do |key, value|
  47. values_match?(value, actual.attributes[key])
  48. end
  49. end
  50. 1 def unmatched_attributes
  51. attributes.stringify_keys.reject do |key, value|
  52. values_match?(value, actual.attributes[key])
  53. end
  54. end
  55. end
  56. # @api public
  57. # Passes if actual is an instance of `model_class` and returns `true` for
  58. # `new_record?`. Typically used to specify instance variables assigned to
  59. # views by controller actions
  60. #
  61. # Use the `with` method to specify the specific attributes to match on the
  62. # new record.
  63. #
  64. # @example
  65. # get :new
  66. # assigns(:thing).should be_a_new(Thing)
  67. #
  68. # post :create, :thing => { :name => "Illegal Value" }
  69. # assigns(:thing).should be_a_new(Thing).with(:name => nil)
  70. 1 def be_a_new(model_class)
  71. BeANew.new(model_class)
  72. end
  73. end
  74. end
  75. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_new_record.rb

66.67% lines covered

12 relevant lines. 8 lines covered and 4 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. # @private
  5. 1 class BeANewRecord < RSpec::Rails::Matchers::BaseMatcher
  6. 1 def matches?(actual)
  7. actual.new_record?
  8. end
  9. 1 def failure_message
  10. "expected #{actual.inspect} to be a new record, but was persisted"
  11. end
  12. 1 def failure_message_when_negated
  13. "expected #{actual.inspect} to be persisted, but was a new record"
  14. end
  15. end
  16. # @api public
  17. # Passes if actual returns `true` for `new_record?`.
  18. #
  19. # @example
  20. # get :new
  21. # expect(assigns(:thing)).to be_new_record
  22. 1 def be_new_record
  23. BeANewRecord.new
  24. end
  25. end
  26. end
  27. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/be_valid.rb

61.9% lines covered

21 relevant lines. 13 lines covered and 8 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. # @private
  5. 1 class BeValid < RSpec::Matchers::BuiltIn::Be
  6. 1 def initialize(*args)
  7. 3 @args = args
  8. end
  9. 1 def matches?(actual)
  10. 3 @actual = actual
  11. 3 actual.valid?(*@args)
  12. end
  13. 1 def failure_message
  14. message = "expected #{actual.inspect} to be valid"
  15. if actual.respond_to?(:errors) && actual.method(:errors).arity < 1
  16. errors = if actual.errors.respond_to?(:full_messages)
  17. actual.errors.full_messages
  18. else
  19. actual.errors
  20. end
  21. message << ", but got errors: #{errors.map(&:to_s).join(', ')}"
  22. end
  23. message
  24. end
  25. 1 def failure_message_when_negated
  26. "expected #{actual.inspect} not to be valid"
  27. end
  28. end
  29. # @api public
  30. # Passes if the given model instance's `valid?` method is true, meaning
  31. # all of the `ActiveModel::Validations` passed and no errors exist. If a
  32. # message is not given, a default message is shown listing each error.
  33. #
  34. # @example
  35. # thing = Thing.new
  36. # expect(thing).to be_valid
  37. 1 def be_valid(*args)
  38. 3 BeValid.new(*args)
  39. end
  40. end
  41. end
  42. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_enqueued_mail.rb

39.29% lines covered

84 relevant lines. 33 lines covered and 51 lines missed.
    
  1. # We require the minimum amount of rspec-mocks possible to avoid
  2. # conflicts with other mocking frameworks.
  3. # See: https://github.com/rspec/rspec-rails/issues/2252
  4. 1 require "rspec/mocks/argument_matchers"
  5. 1 require "rspec/rails/matchers/active_job"
  6. 1 module RSpec
  7. 1 module Rails
  8. 1 module Matchers
  9. # Matcher class for `have_enqueued_mail`. Should not be instantiated directly.
  10. #
  11. # @private
  12. # @see RSpec::Rails::Matchers#have_enqueued_mail
  13. 1 class HaveEnqueuedMail < ActiveJob::HaveEnqueuedJob
  14. 1 MAILER_JOB_METHOD = 'deliver_now'.freeze
  15. 1 include RSpec::Mocks::ArgumentMatchers
  16. 1 def initialize(mailer_class, method_name)
  17. super(nil)
  18. @mailer_class = mailer_class
  19. @method_name = method_name
  20. @mail_args = []
  21. end
  22. 1 def description
  23. "enqueues #{mailer_class_name}.#{@method_name}"
  24. end
  25. 1 def with(*args, &block)
  26. @mail_args = args
  27. block.nil? ? super : super(&yield_mail_args(block))
  28. end
  29. 1 def matches?(block)
  30. raise ArgumentError, 'have_enqueued_mail and enqueue_mail only work with block arguments' unless block.respond_to?(:call)
  31. check_active_job_adapter
  32. super
  33. end
  34. 1 def failure_message
  35. "expected to enqueue #{base_message}".tap do |msg|
  36. msg << "\n#{unmatching_mail_jobs_message}" if unmatching_mail_jobs.any?
  37. end
  38. end
  39. 1 def failure_message_when_negated
  40. "expected not to enqueue #{base_message}"
  41. end
  42. 1 private
  43. 1 def base_message
  44. [mailer_class_name, @method_name].compact.join('.').tap do |msg|
  45. msg << " #{expected_count_message}"
  46. msg << " with #{@mail_args}," if @mail_args.any?
  47. msg << " on queue #{@queue}," if @queue
  48. msg << " at #{@at.inspect}," if @at
  49. msg << " but enqueued #{@matching_jobs.size}"
  50. end
  51. end
  52. 1 def expected_count_message
  53. "#{message_expectation_modifier} #{@expected_number} #{@expected_number == 1 ? 'time' : 'times'}"
  54. end
  55. 1 def mailer_class_name
  56. @mailer_class ? @mailer_class.name : 'ActionMailer::Base'
  57. end
  58. 1 def job_match?(job)
  59. legacy_mail?(job) || parameterized_mail?(job) || unified_mail?(job)
  60. end
  61. 1 def arguments_match?(job)
  62. @args =
  63. if @mail_args.any?
  64. base_mailer_args + @mail_args
  65. elsif @mailer_class && @method_name
  66. base_mailer_args + [any_args]
  67. elsif @mailer_class
  68. [mailer_class_name, any_args]
  69. else
  70. []
  71. end
  72. super(job)
  73. end
  74. 1 def base_mailer_args
  75. [mailer_class_name, @method_name.to_s, MAILER_JOB_METHOD]
  76. end
  77. 1 def yield_mail_args(block)
  78. proc { |*job_args| block.call(*(job_args - base_mailer_args)) }
  79. end
  80. 1 def check_active_job_adapter
  81. return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
  82. raise StandardError, "To use HaveEnqueuedMail matcher set `ActiveJob::Base.queue_adapter = :test`"
  83. end
  84. 1 def unmatching_mail_jobs
  85. @unmatching_jobs.select do |job|
  86. job_match?(job)
  87. end
  88. end
  89. 1 def unmatching_mail_jobs_message
  90. msg = "Queued deliveries:"
  91. unmatching_mail_jobs.each do |job|
  92. msg << "\n #{mail_job_message(job)}"
  93. end
  94. msg
  95. end
  96. 1 def mail_job_message(job)
  97. mailer_method = job[:args][0..1].join('.')
  98. mailer_args = job[:args][3..-1]
  99. msg_parts = []
  100. msg_parts << "with #{mailer_args}" if mailer_args.any?
  101. msg_parts << "on queue #{job[:queue]}" if job[:queue] && job[:queue] != 'mailers'
  102. msg_parts << "at #{Time.at(job[:at])}" if job[:at]
  103. "#{mailer_method} #{msg_parts.join(', ')}".strip
  104. end
  105. 1 def legacy_mail?(job)
  106. job[:job] <= ActionMailer::DeliveryJob
  107. end
  108. 1 def parameterized_mail?(job)
  109. RSpec::Rails::FeatureCheck.has_action_mailer_parameterized? && job[:job] <= ActionMailer::Parameterized::DeliveryJob
  110. end
  111. 1 def unified_mail?(job)
  112. RSpec::Rails::FeatureCheck.has_action_mailer_unified_delivery? && job[:job] <= ActionMailer::MailDeliveryJob
  113. end
  114. end
  115. # @api public
  116. # Passes if an email has been enqueued inside block.
  117. # May chain with to specify expected arguments.
  118. # May chain at_least, at_most or exactly to specify a number of times.
  119. # May chain at to specify a send time.
  120. # May chain on_queue to specify a queue.
  121. #
  122. # @example
  123. # expect {
  124. # MyMailer.welcome(user).deliver_later
  125. # }.to have_enqueued_mail
  126. #
  127. # expect {
  128. # MyMailer.welcome(user).deliver_later
  129. # }.to have_enqueued_mail(MyMailer)
  130. #
  131. # expect {
  132. # MyMailer.welcome(user).deliver_later
  133. # }.to have_enqueued_mail(MyMailer, :welcome)
  134. #
  135. # # Using alias
  136. # expect {
  137. # MyMailer.welcome(user).deliver_later
  138. # }.to enqueue_mail(MyMailer, :welcome)
  139. #
  140. # expect {
  141. # MyMailer.welcome(user).deliver_later
  142. # }.to have_enqueued_mail(MyMailer, :welcome).with(user)
  143. #
  144. # expect {
  145. # MyMailer.welcome(user).deliver_later
  146. # MyMailer.welcome(user).deliver_later
  147. # }.to have_enqueued_mail(MyMailer, :welcome).at_least(:once)
  148. #
  149. # expect {
  150. # MyMailer.welcome(user).deliver_later
  151. # }.to have_enqueued_mail(MyMailer, :welcome).at_most(:twice)
  152. #
  153. # expect {
  154. # MyMailer.welcome(user).deliver_later(wait_until: Date.tomorrow.noon)
  155. # }.to have_enqueued_mail(MyMailer, :welcome).at(Date.tomorrow.noon)
  156. #
  157. # expect {
  158. # MyMailer.welcome(user).deliver_later(queue: :urgent_mail)
  159. # }.to have_enqueued_mail(MyMailer, :welcome).on_queue(:urgent_mail)
  160. 1 def have_enqueued_mail(mailer_class = nil, mail_method_name = nil)
  161. HaveEnqueuedMail.new(mailer_class, mail_method_name)
  162. end
  163. 1 alias_method :have_enqueued_email, :have_enqueued_mail
  164. 1 alias_method :enqueue_mail, :have_enqueued_mail
  165. 1 alias_method :enqueue_email, :have_enqueued_mail
  166. end
  167. end
  168. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_http_status.rb

48.39% lines covered

124 relevant lines. 60 lines covered and 64 lines missed.
    
  1. # The following code inspired and modified from Rails' `assert_response`:
  2. #
  3. # https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/assertions/response.rb#L22-L38
  4. #
  5. # Thank you to all the Rails devs who did the heavy lifting on this!
  6. 1 module RSpec
  7. 1 module Rails
  8. 1 module Matchers
  9. # Namespace for various implementations of `have_http_status`.
  10. #
  11. # @api private
  12. 1 module HaveHttpStatus
  13. # Instantiates an instance of the proper matcher based on the provided
  14. # `target`.
  15. #
  16. # @param target [Object] expected http status or code
  17. # @return response matcher instance
  18. 1 def self.matcher_for_status(target)
  19. 1 if GenericStatus.valid_statuses.include?(target)
  20. GenericStatus.new(target)
  21. 1 elsif Symbol === target
  22. SymbolicStatus.new(target)
  23. else
  24. 1 NumericCode.new(target)
  25. end
  26. end
  27. # @api private
  28. # Conversion function to coerce the provided object into an
  29. # `ActionDispatch::TestResponse`.
  30. #
  31. # @param obj [Object] object to convert to a response
  32. # @return [ActionDispatch::TestResponse]
  33. 1 def as_test_response(obj)
  34. 1 if ::ActionDispatch::Response === obj
  35. 1 ::ActionDispatch::TestResponse.from_response(obj)
  36. elsif ::ActionDispatch::TestResponse === obj
  37. obj
  38. elsif obj.respond_to?(:status_code) && obj.respond_to?(:response_headers)
  39. # Acts As Capybara Session
  40. # Hack to support `Capybara::Session` without having to load
  41. # Capybara or catch `NameError`s for the undefined constants
  42. obj = ActionDispatch::Response.new.tap do |resp|
  43. resp.status = obj.status_code
  44. resp.headers.clear
  45. resp.headers.merge!(obj.response_headers)
  46. resp.body = obj.body
  47. resp.request = ActionDispatch::Request.new({})
  48. end
  49. ::ActionDispatch::TestResponse.from_response(obj)
  50. else
  51. raise TypeError, "Invalid response type: #{obj}"
  52. end
  53. end
  54. 1 module_function :as_test_response
  55. # @return [String, nil] a formatted failure message if
  56. # `@invalid_response` is present, `nil` otherwise
  57. 1 def invalid_response_type_message
  58. return unless @invalid_response
  59. "expected a response object, but an instance of " \
  60. "#{@invalid_response.class} was received"
  61. end
  62. # @api private
  63. # Provides an implementation for `have_http_status` matching against
  64. # numeric http status codes.
  65. #
  66. # Not intended to be instantiated directly.
  67. #
  68. # @example
  69. # expect(response).to have_http_status(404)
  70. #
  71. # @see RSpec::Rails::Matchers#have_http_status
  72. 1 class NumericCode < RSpec::Rails::Matchers::BaseMatcher
  73. 1 include HaveHttpStatus
  74. 1 def initialize(code)
  75. 1 @expected = code.to_i
  76. 1 @actual = nil
  77. 1 @invalid_response = nil
  78. end
  79. # @param [Object] response object providing an http code to match
  80. # @return [Boolean] `true` if the numeric code matched the `response` code
  81. 1 def matches?(response)
  82. 1 test_response = as_test_response(response)
  83. 1 @actual = test_response.response_code.to_i
  84. 1 expected == @actual
  85. rescue TypeError => _ignored
  86. @invalid_response = response
  87. false
  88. end
  89. # @return [String]
  90. 1 def description
  91. "respond with numeric status code #{expected}"
  92. end
  93. # @return [String] explaining why the match failed
  94. 1 def failure_message
  95. invalid_response_type_message ||
  96. "expected the response to have status code #{expected.inspect}" \
  97. " but it was #{actual.inspect}"
  98. end
  99. # @return [String] explaining why the match failed
  100. 1 def failure_message_when_negated
  101. invalid_response_type_message ||
  102. "expected the response not to have status code " \
  103. "#{expected.inspect} but it did"
  104. end
  105. end
  106. # @api private
  107. # Provides an implementation for `have_http_status` matching against
  108. # Rack symbol http status codes.
  109. #
  110. # Not intended to be instantiated directly.
  111. #
  112. # @example
  113. # expect(response).to have_http_status(:created)
  114. #
  115. # @see RSpec::Rails::Matchers#have_http_status
  116. # @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
  117. 1 class SymbolicStatus < RSpec::Rails::Matchers::BaseMatcher
  118. 1 include HaveHttpStatus
  119. 1 def initialize(status)
  120. @expected_status = status
  121. @actual = nil
  122. @invalid_response = nil
  123. set_expected_code!
  124. end
  125. # @param [Object] response object providing an http code to match
  126. # @return [Boolean] `true` if Rack's associated numeric HTTP code matched
  127. # the `response` code
  128. 1 def matches?(response)
  129. test_response = as_test_response(response)
  130. @actual = test_response.response_code
  131. expected == @actual
  132. rescue TypeError => _ignored
  133. @invalid_response = response
  134. false
  135. end
  136. # @return [String]
  137. 1 def description
  138. "respond with status code #{pp_expected}"
  139. end
  140. # @return [String] explaining why the match failed
  141. 1 def failure_message
  142. invalid_response_type_message ||
  143. "expected the response to have status code #{pp_expected} but it" \
  144. " was #{pp_actual}"
  145. end
  146. # @return [String] explaining why the match failed
  147. 1 def failure_message_when_negated
  148. invalid_response_type_message ||
  149. "expected the response not to have status code #{pp_expected} " \
  150. "but it did"
  151. end
  152. # The initialized expected status symbol
  153. 1 attr_reader :expected_status
  154. 1 private :expected_status
  155. 1 private
  156. # @return [Symbol] representing the actual http numeric code
  157. 1 def actual_status
  158. return unless actual
  159. @actual_status ||= compute_status_from(actual)
  160. end
  161. # Reverse lookup of the Rack status code symbol based on the numeric
  162. # http code
  163. #
  164. # @param code [Fixnum] http status code to look up
  165. # @return [Symbol] representing the http numeric code
  166. 1 def compute_status_from(code)
  167. status, _ = Rack::Utils::SYMBOL_TO_STATUS_CODE.find do |_, c|
  168. c == code
  169. end
  170. status
  171. end
  172. # @return [String] pretty format the actual response status
  173. 1 def pp_actual
  174. pp_status(actual_status, actual)
  175. end
  176. # @return [String] pretty format the expected status and associated code
  177. 1 def pp_expected
  178. pp_status(expected_status, expected)
  179. end
  180. # @return [String] pretty format the actual response status
  181. 1 def pp_status(status, code)
  182. if status
  183. "#{status.inspect} (#{code})"
  184. else
  185. code.to_s
  186. end
  187. end
  188. # Sets `expected` to the numeric http code based on the Rack
  189. # `expected_status` status
  190. #
  191. # @see Rack::Utils::SYMBOL_TO_STATUS_CODE
  192. # @raise [ArgumentError] if an associated code could not be found
  193. 1 def set_expected_code!
  194. @expected ||=
  195. Rack::Utils::SYMBOL_TO_STATUS_CODE.fetch(expected_status) do
  196. raise ArgumentError,
  197. "Invalid HTTP status: #{expected_status.inspect}"
  198. end
  199. end
  200. end
  201. # @api private
  202. # Provides an implementation for `have_http_status` matching against
  203. # `ActionDispatch::TestResponse` http status category queries.
  204. #
  205. # Not intended to be instantiated directly.
  206. #
  207. # @example
  208. # expect(response).to have_http_status(:success)
  209. # expect(response).to have_http_status(:error)
  210. # expect(response).to have_http_status(:missing)
  211. # expect(response).to have_http_status(:redirect)
  212. #
  213. # @see RSpec::Rails::Matchers#have_http_status
  214. # @see https://github.com/rails/rails/blob/6-0-stable/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
  215. 1 class GenericStatus < RSpec::Rails::Matchers::BaseMatcher
  216. 1 include HaveHttpStatus
  217. # @return [Array<Symbol>] of status codes which represent a HTTP status
  218. # code "group"
  219. # @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
  220. 1 def self.valid_statuses
  221. 1 [
  222. :error, :success, :missing,
  223. :server_error, :successful, :not_found,
  224. :redirect
  225. ]
  226. end
  227. 1 def initialize(type)
  228. unless self.class.valid_statuses.include?(type)
  229. raise ArgumentError, "Invalid generic HTTP status: #{type.inspect}"
  230. end
  231. @expected = type
  232. @actual = nil
  233. @invalid_response = nil
  234. end
  235. # @return [Boolean] `true` if Rack's associated numeric HTTP code matched
  236. # the `response` code or the named response status
  237. 1 def matches?(response)
  238. test_response = as_test_response(response)
  239. @actual = test_response.response_code
  240. check_expected_status(test_response, expected)
  241. rescue TypeError => _ignored
  242. @invalid_response = response
  243. false
  244. end
  245. # @return [String]
  246. 1 def description
  247. "respond with #{type_message}"
  248. end
  249. # @return [String] explaining why the match failed
  250. 1 def failure_message
  251. invalid_response_type_message ||
  252. "expected the response to have #{type_message} but it was #{actual}"
  253. end
  254. # @return [String] explaining why the match failed
  255. 1 def failure_message_when_negated
  256. invalid_response_type_message ||
  257. "expected the response not to have #{type_message} but it was #{actual}"
  258. end
  259. 1 protected
  260. RESPONSE_METHODS = {
  261. 1 success: 'successful',
  262. error: 'server_error',
  263. missing: 'not_found'
  264. }.freeze
  265. 1 def check_expected_status(test_response, expected)
  266. test_response.send(
  267. "#{RESPONSE_METHODS.fetch(expected, expected)}?")
  268. end
  269. 1 private
  270. # @return [String] formating the expected status and associated code(s)
  271. 1 def type_message
  272. @type_message ||= (expected == :error ? "an error" : "a #{expected}") +
  273. " status code (#{type_codes})"
  274. end
  275. # @return [String] formatting the associated code(s) for the various
  276. # status code "groups"
  277. # @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
  278. # @see https://github.com/rack/rack/blob/master/lib/rack/response.rb `Rack::Response`
  279. 1 def type_codes
  280. # At the time of this commit the most recent version of
  281. # `ActionDispatch::TestResponse` defines the following aliases:
  282. #
  283. # alias_method :success?, :successful?
  284. # alias_method :missing?, :not_found?
  285. # alias_method :redirect?, :redirection?
  286. # alias_method :error?, :server_error?
  287. #
  288. # It's parent `ActionDispatch::Response` includes
  289. # `Rack::Response::Helpers` which defines the aliased methods as:
  290. #
  291. # def successful?; status >= 200 && status < 300; end
  292. # def redirection?; status >= 300 && status < 400; end
  293. # def server_error?; status >= 500 && status < 600; end
  294. # def not_found?; status == 404; end
  295. #
  296. # @see https://github.com/rails/rails/blob/ca200378/actionpack/lib/action_dispatch/testing/test_response.rb#L17-L27
  297. # @see https://github.com/rails/rails/blob/ca200378/actionpack/lib/action_dispatch/http/response.rb#L74
  298. # @see https://github.com/rack/rack/blob/ce4a3959/lib/rack/response.rb#L119-L122
  299. @type_codes ||= case expected
  300. when :error, :server_error
  301. "5xx"
  302. when :success, :successful
  303. "2xx"
  304. when :missing, :not_found
  305. "404"
  306. when :redirect
  307. "3xx"
  308. end
  309. end
  310. end
  311. end
  312. # @api public
  313. # Passes if `response` has a matching HTTP status code.
  314. #
  315. # The following symbolic status codes are allowed:
  316. #
  317. # - `Rack::Utils::SYMBOL_TO_STATUS_CODE`
  318. # - One of the defined `ActionDispatch::TestResponse` aliases:
  319. # - `:error`
  320. # - `:missing`
  321. # - `:redirect`
  322. # - `:success`
  323. #
  324. # @example Accepts numeric and symbol statuses
  325. # expect(response).to have_http_status(404)
  326. # expect(response).to have_http_status(:created)
  327. # expect(response).to have_http_status(:success)
  328. # expect(response).to have_http_status(:error)
  329. # expect(response).to have_http_status(:missing)
  330. # expect(response).to have_http_status(:redirect)
  331. #
  332. # @example Works with standard `response` objects and Capybara's `page`
  333. # expect(response).to have_http_status(404)
  334. # expect(page).to have_http_status(:created)
  335. #
  336. # @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
  337. # @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
  338. 1 def have_http_status(target)
  339. 1 raise ArgumentError, "Invalid HTTP status: nil" unless target
  340. 1 HaveHttpStatus.matcher_for_status(target)
  341. end
  342. end
  343. end
  344. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/have_rendered.rb

42.86% lines covered

28 relevant lines. 12 lines covered and 16 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. # Matcher for template rendering.
  5. 1 module RenderTemplate
  6. # @private
  7. 1 class RenderTemplateMatcher < RSpec::Rails::Matchers::BaseMatcher
  8. 1 def initialize(scope, expected, message = nil)
  9. @expected = Symbol === expected ? expected.to_s : expected
  10. @message = message
  11. @scope = scope
  12. @redirect_is = nil
  13. end
  14. # @api private
  15. 1 def matches?(*)
  16. match_check = match_unless_raises ActiveSupport::TestCase::Assertion do
  17. @scope.assert_template expected, @message
  18. end
  19. check_redirect unless match_check
  20. match_check
  21. end
  22. # Uses normalize_argument_to_redirection to find and format
  23. # the redirect location. normalize_argument_to_redirection is private
  24. # in ActionDispatch::Assertions::ResponseAssertions so we call it
  25. # here using #send. This will keep the error message format consistent
  26. # @api private
  27. 1 def check_redirect
  28. response = @scope.response
  29. return unless response.respond_to?(:redirect?) && response.redirect?
  30. @redirect_is = @scope.send(:normalize_argument_to_redirection, response.location)
  31. end
  32. # @api private
  33. 1 def failure_message
  34. if @redirect_is
  35. rescued_exception.message[/(.*?)( but|$)/, 1] +
  36. " but was a redirect to <#{@redirect_is}>"
  37. else
  38. rescued_exception.message
  39. end
  40. end
  41. # @api private
  42. 1 def failure_message_when_negated
  43. "expected not to render #{expected.inspect}, but did"
  44. end
  45. end
  46. # Delegates to `assert_template`.
  47. #
  48. # @example
  49. # expect(response).to have_rendered("new")
  50. 1 def have_rendered(options, message = nil)
  51. RenderTemplateMatcher.new(self, options, message)
  52. end
  53. 1 alias_method :render_template, :have_rendered
  54. end
  55. end
  56. end
  57. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/redirect_to.rb

58.82% lines covered

17 relevant lines. 10 lines covered and 7 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. # Matcher for redirects.
  5. 1 module RedirectTo
  6. # @private
  7. 1 class RedirectTo < RSpec::Rails::Matchers::BaseMatcher
  8. 1 def initialize(scope, expected)
  9. @expected = expected
  10. @scope = scope
  11. end
  12. 1 def matches?(_)
  13. match_unless_raises ActiveSupport::TestCase::Assertion do
  14. @scope.assert_redirected_to(@expected)
  15. end
  16. end
  17. 1 def failure_message
  18. rescued_exception.message
  19. end
  20. 1 def failure_message_when_negated
  21. "expected not to redirect to #{@expected.inspect}, but did"
  22. end
  23. end
  24. # Delegates to `assert_redirected_to`.
  25. #
  26. # @example
  27. # expect(response).to redirect_to(:action => "new")
  28. 1 def redirect_to(target)
  29. RedirectTo.new(self, target)
  30. end
  31. end
  32. end
  33. end
  34. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/relation_match_array.rb

100.0% lines covered

2 relevant lines. 2 lines covered and 0 lines missed.
    
  1. 1 if defined?(ActiveRecord::Relation) && defined?(RSpec::Matchers::BuiltIn::OperatorMatcher) # RSpec 4 removed OperatorMatcher
  2. 1 RSpec::Matchers::BuiltIn::OperatorMatcher.register(ActiveRecord::Relation, '=~', RSpec::Matchers::BuiltIn::ContainExactly)
  3. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/matchers/routing_matchers.rb

48.89% lines covered

45 relevant lines. 22 lines covered and 23 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. 1 module Matchers
  4. # Matchers to help with specs for routing code.
  5. 1 module RoutingMatchers
  6. 1 extend RSpec::Matchers::DSL
  7. # @private
  8. 1 class RouteToMatcher < RSpec::Rails::Matchers::BaseMatcher
  9. 1 def initialize(scope, *expected)
  10. @scope = scope
  11. @expected = expected[1] || {}
  12. if Hash === expected[0]
  13. @expected.merge!(expected[0])
  14. else
  15. controller, action = expected[0].split('#')
  16. @expected.merge!(controller: controller, action: action)
  17. end
  18. end
  19. 1 def matches?(verb_to_path_map)
  20. @actual = verb_to_path_map
  21. # assert_recognizes does not consider ActionController::RoutingError an
  22. # assertion failure, so we have to capture that and Assertion here.
  23. match_unless_raises ActiveSupport::TestCase::Assertion, ActionController::RoutingError do
  24. path, query = *verb_to_path_map.values.first.split('?')
  25. @scope.assert_recognizes(
  26. @expected,
  27. {method: verb_to_path_map.keys.first, path: path},
  28. Rack::Utils.parse_nested_query(query)
  29. )
  30. end
  31. end
  32. 1 def failure_message
  33. rescued_exception.message
  34. end
  35. 1 def failure_message_when_negated
  36. "expected #{@actual.inspect} not to route to #{@expected.inspect}"
  37. end
  38. 1 def description
  39. "route #{@actual.inspect} to #{@expected.inspect}"
  40. end
  41. end
  42. # Delegates to `assert_recognizes`. Supports short-hand controller/action
  43. # declarations (e.g. `"controller#action"`).
  44. #
  45. # @example
  46. #
  47. # expect(get: "/things/special").to route_to(
  48. # controller: "things",
  49. # action: "special"
  50. # )
  51. #
  52. # expect(get: "/things/special").to route_to("things#special")
  53. #
  54. # @see https://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes
  55. 1 def route_to(*expected)
  56. RouteToMatcher.new(self, *expected)
  57. end
  58. # @private
  59. 1 class BeRoutableMatcher < RSpec::Rails::Matchers::BaseMatcher
  60. 1 def initialize(scope)
  61. @scope = scope
  62. end
  63. 1 def matches?(path)
  64. @actual = path
  65. match_unless_raises ActionController::RoutingError do
  66. @routing_options = @scope.routes.recognize_path(
  67. path.values.first, method: path.keys.first
  68. )
  69. end
  70. end
  71. 1 def failure_message
  72. "expected #{@actual.inspect} to be routable"
  73. end
  74. 1 def failure_message_when_negated
  75. "expected #{@actual.inspect} not to be routable, but it routes to #{@routing_options.inspect}"
  76. end
  77. 1 def description
  78. "be routable"
  79. end
  80. end
  81. # Passes if the route expression is recognized by the Rails router based on
  82. # the declarations in `config/routes.rb`. Delegates to
  83. # `RouteSet#recognize_path`.
  84. #
  85. # @example You can use route helpers provided by rspec-rails.
  86. # expect(get: "/a/path").to be_routable
  87. # expect(post: "/another/path").to be_routable
  88. # expect(put: "/yet/another/path").to be_routable
  89. 1 def be_routable
  90. BeRoutableMatcher.new(self)
  91. end
  92. # Helpers for matching different route types.
  93. 1 module RouteHelpers
  94. # @!method get
  95. # @!method post
  96. # @!method put
  97. # @!method patch
  98. # @!method delete
  99. # @!method options
  100. # @!method head
  101. #
  102. # Shorthand method for matching this type of route.
  103. 1 %w[get post put patch delete options head].each do |method|
  104. 7 define_method method do |path|
  105. {method.to_sym => path}
  106. end
  107. end
  108. end
  109. end
  110. end
  111. end
  112. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/vendor/capybara.rb

16.67% lines covered

18 relevant lines. 3 lines covered and 15 lines missed.
    
  1. begin
  2. 1 require 'capybara/rspec'
  3. rescue LoadError
  4. end
  5. begin
  6. 1 require 'capybara/rails'
  7. rescue LoadError
  8. end
  9. 1 if defined?(Capybara)
  10. RSpec.configure do |c|
  11. if defined?(Capybara::DSL)
  12. c.include Capybara::DSL, type: :feature
  13. if defined?(ActionPack) && ActionPack::VERSION::STRING >= "5.1"
  14. c.include Capybara::DSL, type: :system
  15. end
  16. end
  17. if defined?(Capybara::RSpecMatchers)
  18. c.include Capybara::RSpecMatchers, type: :view
  19. c.include Capybara::RSpecMatchers, type: :helper
  20. c.include Capybara::RSpecMatchers, type: :mailer
  21. c.include Capybara::RSpecMatchers, type: :controller
  22. c.include Capybara::RSpecMatchers, type: :feature
  23. c.include Capybara::RSpecMatchers, type: :system
  24. end
  25. unless defined?(Capybara::RSpecMatchers) || defined?(Capybara::DSL)
  26. c.include Capybara, type: :request
  27. c.include Capybara, type: :controller
  28. end
  29. end
  30. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_assigns.rb

61.54% lines covered

13 relevant lines. 8 lines covered and 5 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # Helpers for making instance variables available to views.
  4. 1 module ViewAssigns
  5. # Assigns a value to an instance variable in the scope of the
  6. # view being rendered.
  7. #
  8. # @example
  9. #
  10. # assign(:widget, stub_model(Widget))
  11. 1 def assign(key, value)
  12. _encapsulated_assigns[key] = value
  13. end
  14. # Compat-shim for AbstractController::Rendering#view_assigns
  15. #
  16. # _assigns was deprecated in favor of view_assigns after
  17. # Rails-3.0.0 was released. Since we are not able to predict when
  18. # the _assigns/view_assigns patch will be released (I thought it
  19. # would have been in 3.0.1, but 3.0.1 bypassed this change for a
  20. # security fix), this bit ensures that we do the right thing without
  21. # knowing anything about the Rails version we are dealing with.
  22. #
  23. # Once that change _is_ released, this can be changed to something
  24. # that checks for the Rails version when the module is being
  25. # interpreted, as it was before commit dd0095.
  26. 1 def view_assigns
  27. super.merge(_encapsulated_assigns)
  28. rescue
  29. _assigns
  30. end
  31. # @private
  32. 1 def _assigns
  33. super.merge(_encapsulated_assigns)
  34. end
  35. 1 private
  36. 1 def _encapsulated_assigns
  37. @_encapsulated_assigns ||= {}
  38. end
  39. end
  40. end
  41. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_path_builder.rb

62.5% lines covered

8 relevant lines. 5 lines covered and 3 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # Builds paths for view specs using a particular route set.
  4. 1 class ViewPathBuilder
  5. 1 def initialize(route_set)
  6. self.class.send(:include, route_set.url_helpers)
  7. end
  8. # Given a hash of parameters, build a view path, if possible.
  9. # Returns nil if no path can be built from the given params.
  10. #
  11. # @example
  12. # # path can be built because all required params are present in the hash
  13. # view_path_builder = ViewPathBuilder.new(::Rails.application.routes)
  14. # view_path_builder.path_for({ :controller => 'posts', :action => 'show', :id => '54' })
  15. # # => "/post/54"
  16. #
  17. # @example
  18. # # path cannot be built because the params are missing a required element (:id)
  19. # view_path_builder.path_for({ :controller => 'posts', :action => 'delete' })
  20. # # => ActionController::UrlGenerationError: No route matches {:action=>"delete", :controller=>"posts"}
  21. 1 def path_for(path_params)
  22. url_for(path_params.merge(only_path: true))
  23. rescue => e
  24. e.message
  25. end
  26. end
  27. end
  28. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_rendering.rb

48.57% lines covered

70 relevant lines. 34 lines covered and 36 lines missed.
    
  1. 1 require 'action_view/testing/resolvers'
  2. 1 module RSpec
  3. 1 module Rails
  4. # @api public
  5. # Helpers for optionally rendering views in controller specs.
  6. 1 module ViewRendering
  7. 1 extend ActiveSupport::Concern
  8. # @!attribute [r]
  9. # Returns the controller object instance under test.
  10. 1 attr_reader :controller
  11. # @private
  12. 1 attr_writer :controller
  13. 1 private :controller=
  14. # DSL methods
  15. 1 module ClassMethods
  16. # @see RSpec::Rails::ControllerExampleGroup
  17. 1 def render_views(true_or_false = true)
  18. @render_views = true_or_false
  19. end
  20. # @api private
  21. 1 def render_views?
  22. return @render_views if defined?(@render_views)
  23. if superclass.respond_to?(:render_views?)
  24. superclass.render_views?
  25. else
  26. RSpec.configuration.render_views?
  27. end
  28. end
  29. end
  30. # @api private
  31. 1 def render_views?
  32. self.class.render_views? || !controller.class.respond_to?(:view_paths)
  33. end
  34. # @private
  35. 1 class EmptyTemplateResolver
  36. 1 def self.build(path)
  37. if path.is_a?(::ActionView::Resolver)
  38. ResolverDecorator.new(path)
  39. else
  40. FileSystemResolver.new(path)
  41. end
  42. end
  43. 1 def self.nullify_template_rendering(templates)
  44. templates.map do |template|
  45. ::ActionView::Template.new(
  46. "",
  47. template.identifier,
  48. EmptyTemplateHandler,
  49. virtual_path: template.virtual_path,
  50. format: template_format(template),
  51. locals: []
  52. )
  53. end
  54. end
  55. 1 if ::Rails::VERSION::STRING >= '6'
  56. 1 def self.template_format(template)
  57. template.format
  58. end
  59. else
  60. def self.template_format(template)
  61. template.formats
  62. end
  63. end
  64. # Delegates all methods to the submitted resolver and for all methods
  65. # that return a collection of `ActionView::Template` instances, return
  66. # templates with modified source
  67. #
  68. # @private
  69. 1 class ResolverDecorator
  70. 1 def initialize(resolver)
  71. @resolver = resolver
  72. end
  73. 1 def method_missing(name, *args, &block)
  74. result = @resolver.send(name, *args, &block)
  75. nullify_templates(result)
  76. end
  77. 1 private
  78. 1 def nullify_templates(collection)
  79. return collection unless collection.is_a?(Enumerable)
  80. return collection unless collection.all? { |element| element.is_a?(::ActionView::Template) }
  81. EmptyTemplateResolver.nullify_template_rendering(collection)
  82. end
  83. end
  84. # Delegates find_templates to the submitted path set and then returns
  85. # templates with modified source
  86. #
  87. # @private
  88. 1 class FileSystemResolver < ::ActionView::FileSystemResolver
  89. 1 private
  90. 1 def find_templates(*args)
  91. templates = super
  92. EmptyTemplateResolver.nullify_template_rendering(templates)
  93. end
  94. end
  95. end
  96. # @private
  97. 1 class EmptyTemplateHandler
  98. 1 def self.call(_template, _source = nil)
  99. ::Rails.logger.info(" Template rendering was prevented by rspec-rails. Use `render_views` to verify rendered view contents if necessary.")
  100. %("")
  101. end
  102. end
  103. # Used to null out view rendering in controller specs.
  104. #
  105. # @private
  106. 1 module EmptyTemplates
  107. 1 def prepend_view_path(new_path)
  108. lookup_context.view_paths.unshift(*_path_decorator(*new_path))
  109. end
  110. 1 def append_view_path(new_path)
  111. lookup_context.view_paths.push(*_path_decorator(*new_path))
  112. end
  113. 1 private
  114. 1 def _path_decorator(*paths)
  115. paths.map { |path| EmptyTemplateResolver.build(path) }
  116. end
  117. end
  118. # @private
  119. 1 RESOLVER_CACHE = Hash.new do |hash, path|
  120. hash[path] = EmptyTemplateResolver.build(path)
  121. end
  122. 1 included do
  123. before do
  124. unless render_views?
  125. @_original_path_set = controller.class.view_paths
  126. path_set = @_original_path_set.map { |resolver| RESOLVER_CACHE[resolver] }
  127. controller.class.view_paths = path_set
  128. controller.extend(EmptyTemplates)
  129. end
  130. end
  131. after do
  132. controller.class.view_paths = @_original_path_set unless render_views?
  133. end
  134. end
  135. end
  136. end
  137. end

vender/bundle/ruby/2.6.0/gems/rspec-rails-4.0.2/lib/rspec/rails/view_spec_methods.rb

35.29% lines covered

17 relevant lines. 6 lines covered and 11 lines missed.
    
  1. 1 module RSpec
  2. 1 module Rails
  3. # Adds methods (generally to ActionView::TestCase::TestController).
  4. # Intended for use in view specs.
  5. 1 module ViewSpecMethods
  6. 1 module_function
  7. # Adds methods `extra_params=` and `extra_params` to the indicated class.
  8. # When class is `::ActionView::TestCase::TestController`, these methods
  9. # are exposed in view specs on the `controller` object.
  10. 1 def add_to(klass)
  11. return if klass.method_defined?(:extra_params) && klass.method_defined?(:extra_params=)
  12. klass.module_exec do
  13. # Set any extra parameters that rendering a URL for this view
  14. # would require.
  15. #
  16. # @example
  17. #
  18. # # In "spec/views/widgets/show.html.erb_spec.rb":
  19. # before do
  20. # widget = Widget.create!(:name => "slicer")
  21. # controller.extra_params = { :id => widget.id }
  22. # end
  23. def extra_params=(hash)
  24. @extra_params = hash
  25. request.path =
  26. ViewPathBuilder.new(::Rails.application.routes).path_for(
  27. extra_params.merge(request.path_parameters)
  28. )
  29. end
  30. # Use to read extra parameters that are set in the view spec.
  31. #
  32. # @example
  33. #
  34. # # After the before in the above example:
  35. # controller.extra_params
  36. # # => { :id => 4 }
  37. def extra_params
  38. @extra_params ||= {}
  39. @extra_params.dup.freeze
  40. end
  41. end
  42. end
  43. # Removes methods `extra_params=` and `extra_params` from the indicated class.
  44. 1 def remove_from(klass)
  45. klass.module_exec do
  46. undef extra_params= if klass.method_defined?(:extra_params=)
  47. undef extra_params if klass.method_defined?(:extra_params)
  48. end
  49. end
  50. end
  51. end
  52. end

vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/differ.rb

73.64% lines covered

110 relevant lines. 81 lines covered and 29 lines missed.
    
  1. 1 RSpec::Support.require_rspec_support 'encoded_string'
  2. 1 RSpec::Support.require_rspec_support 'hunk_generator'
  3. 1 RSpec::Support.require_rspec_support "object_formatter"
  4. 1 require 'pp'
  5. 1 module RSpec
  6. 1 module Support
  7. # rubocop:disable ClassLength
  8. 1 class Differ
  9. 1 def diff(actual, expected)
  10. 1 diff = ""
  11. 1 unless actual.nil? || expected.nil?
  12. 1 if all_strings?(actual, expected)
  13. if any_multiline_strings?(actual, expected)
  14. diff = diff_as_string(coerce_to_string(actual), coerce_to_string(expected))
  15. end
  16. 1 elsif no_procs?(actual, expected) && no_numbers?(actual, expected)
  17. 1 diff = diff_as_object(actual, expected)
  18. end
  19. end
  20. 1 diff.to_s
  21. end
  22. # rubocop:disable MethodLength
  23. 1 def diff_as_string(actual, expected)
  24. 1 encoding = EncodedString.pick_encoding(actual, expected)
  25. 1 actual = EncodedString.new(actual, encoding)
  26. 1 expected = EncodedString.new(expected, encoding)
  27. 1 output = EncodedString.new("\n", encoding)
  28. 1 hunks = build_hunks(actual, expected)
  29. 1 hunks.each_cons(2) do |prev_hunk, current_hunk|
  30. begin
  31. if current_hunk.overlaps?(prev_hunk)
  32. add_old_hunk_to_hunk(current_hunk, prev_hunk)
  33. else
  34. add_to_output(output, prev_hunk.diff(format_type).to_s)
  35. end
  36. ensure
  37. add_to_output(output, "\n")
  38. end
  39. end
  40. 1 finalize_output(output, hunks.last.diff(format_type).to_s) if hunks.last
  41. 1 color_diff output
  42. rescue Encoding::CompatibilityError
  43. handle_encoding_errors(actual, expected)
  44. end
  45. # rubocop:enable MethodLength
  46. 1 def diff_as_object(actual, expected)
  47. 1 actual_as_string = object_to_string(actual)
  48. 1 expected_as_string = object_to_string(expected)
  49. 1 diff_as_string(actual_as_string, expected_as_string)
  50. end
  51. 1 def color?
  52. 1 @color
  53. end
  54. 1 def initialize(opts={})
  55. 1 @color = opts.fetch(:color, false)
  56. 1 @object_preparer = opts.fetch(:object_preparer, lambda { |string| string })
  57. end
  58. 1 private
  59. 1 def no_procs?(*args)
  60. 3 safely_flatten(args).none? { |a| Proc === a }
  61. end
  62. 1 def all_strings?(*args)
  63. 2 safely_flatten(args).all? { |a| String === a }
  64. end
  65. 1 def any_multiline_strings?(*args)
  66. all_strings?(*args) && safely_flatten(args).any? { |a| multiline?(a) }
  67. end
  68. 1 def no_numbers?(*args)
  69. 3 safely_flatten(args).none? { |a| Numeric === a }
  70. end
  71. 1 def coerce_to_string(string_or_array)
  72. return string_or_array unless Array === string_or_array
  73. diffably_stringify(string_or_array).join("\n")
  74. end
  75. 1 def diffably_stringify(array)
  76. array.map do |entry|
  77. if Array === entry
  78. entry.inspect
  79. else
  80. entry.to_s.gsub("\n", "\\n").gsub("\r", "\\r")
  81. end
  82. end
  83. end
  84. 1 if String.method_defined?(:encoding)
  85. 1 def multiline?(string)
  86. string.include?("\n".encode(string.encoding))
  87. end
  88. else
  89. def multiline?(string)
  90. string.include?("\n")
  91. end
  92. end
  93. 1 def build_hunks(actual, expected)
  94. 1 HunkGenerator.new(actual, expected).hunks
  95. end
  96. 1 def finalize_output(output, final_line)
  97. 1 add_to_output(output, final_line)
  98. 1 add_to_output(output, "\n")
  99. end
  100. 1 def add_to_output(output, string)
  101. 2 output << string
  102. end
  103. 1 def add_old_hunk_to_hunk(hunk, oldhunk)
  104. hunk.merge(oldhunk)
  105. end
  106. 1 def safely_flatten(array)
  107. 3 array = array.flatten(1) until (array == array.flatten(1))
  108. 3 array
  109. end
  110. 1 def format_type
  111. 1 :unified
  112. end
  113. 1 def color(text, color_code)
  114. 4 "\e[#{color_code}m#{text}\e[0m"
  115. end
  116. 1 def red(text)
  117. 1 color(text, 31)
  118. end
  119. 1 def green(text)
  120. 1 color(text, 32)
  121. end
  122. 1 def blue(text)
  123. 1 color(text, 34)
  124. end
  125. 1 def normal(text)
  126. 1 color(text, 0)
  127. end
  128. 1 def color_diff(diff)
  129. 1 return diff unless color?
  130. 1 diff.lines.map do |line|
  131. 4 case line[0].chr
  132. when "+"
  133. 1 green line
  134. when "-"
  135. 1 red line
  136. when "@"
  137. 1 line[1].chr == "@" ? blue(line) : normal(line)
  138. else
  139. 1 normal(line)
  140. end
  141. end.join
  142. end
  143. 1 def object_to_string(object)
  144. 2 object = @object_preparer.call(object)
  145. 2 case object
  146. when Hash
  147. hash_to_string(object)
  148. when Array
  149. PP.pp(ObjectFormatter.prepare_for_inspection(object), "".dup)
  150. when String
  151. object =~ /\n/ ? object : object.inspect
  152. else
  153. 2 PP.pp(object, "".dup)
  154. end
  155. end
  156. 1 def hash_to_string(hash)
  157. formatted_hash = ObjectFormatter.prepare_for_inspection(hash)
  158. formatted_hash.keys.sort_by { |k| k.to_s }.map do |key|
  159. pp_key = PP.singleline_pp(key, "".dup)
  160. pp_value = PP.singleline_pp(formatted_hash[key], "".dup)
  161. "#{pp_key} => #{pp_value},"
  162. end.join("\n")
  163. end
  164. 1 def handle_encoding_errors(actual, expected)
  165. if actual.source_encoding != expected.source_encoding
  166. "Could not produce a diff because the encoding of the actual string " \
  167. "(#{actual.source_encoding}) differs from the encoding of the expected " \
  168. "string (#{expected.source_encoding})"
  169. else
  170. "Could not produce a diff because of the encoding of the string " \
  171. "(#{expected.source_encoding})"
  172. end
  173. end
  174. end
  175. # rubocop:enable ClassLength
  176. end
  177. end

vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/hunk_generator.rb

100.0% lines covered

24 relevant lines. 24 lines covered and 0 lines missed.
    
  1. 1 require 'diff/lcs'
  2. 1 require 'diff/lcs/hunk'
  3. 1 module RSpec
  4. 1 module Support
  5. # @private
  6. 1 class HunkGenerator
  7. 1 def initialize(actual, expected)
  8. 1 @actual = actual
  9. 1 @expected = expected
  10. end
  11. 1 def hunks
  12. 1 @file_length_difference = 0
  13. 1 @hunks ||= diffs.map do |piece|
  14. 1 build_hunk(piece)
  15. end
  16. end
  17. 1 private
  18. 1 def diffs
  19. 1 Diff::LCS.diff(expected_lines, actual_lines)
  20. end
  21. 1 def expected_lines
  22. 4 @expected.split("\n").map! { |e| e.chomp }
  23. end
  24. 1 def actual_lines
  25. 4 @actual.split("\n").map! { |e| e.chomp }
  26. end
  27. 1 def build_hunk(piece)
  28. 1 Diff::LCS::Hunk.new(
  29. expected_lines, actual_lines, piece, context_lines, @file_length_difference
  30. ).tap do |h|
  31. 1 @file_length_difference = h.file_length_difference
  32. end
  33. end
  34. 1 def context_lines
  35. 1 3
  36. end
  37. end
  38. end
  39. end

vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source.rb

97.67% lines covered

43 relevant lines. 42 lines covered and 1 lines missed.
    
  1. 1 RSpec::Support.require_rspec_support 'encoded_string'
  2. 1 RSpec::Support.require_rspec_support 'ruby_features'
  3. 1 module RSpec
  4. 1 module Support
  5. # @private
  6. # Represents a Ruby source file and provides access to AST and tokens.
  7. 1 class Source
  8. 1 attr_reader :source, :path
  9. # This class protects us against having File read and expand_path
  10. # stubbed out within tests.
  11. 1 class File
  12. 1 class << self
  13. 1 [:read, :expand_path].each do |method_name|
  14. 2 define_method(method_name, &::File.method(method_name))
  15. end
  16. end
  17. end
  18. 1 def self.from_file(path)
  19. 3 source = File.read(path)
  20. 3 new(source, path)
  21. end
  22. 1 if String.method_defined?(:encoding)
  23. 1 def initialize(source_string, path=nil)
  24. 3 @source = RSpec::Support::EncodedString.new(source_string, Encoding.default_external)
  25. 3 @path = path ? File.expand_path(path) : '(string)'
  26. end
  27. else # for 1.8.7
  28. skipped # :nocov:
  29. skipped def initialize(source_string, path=nil)
  30. skipped @source = RSpec::Support::EncodedString.new(source_string)
  31. skipped @path = path ? File.expand_path(path) : '(string)'
  32. skipped end
  33. skipped # :nocov:
  34. end
  35. 1 def lines
  36. 6 @lines ||= source.split("\n")
  37. end
  38. 1 def inspect
  39. "#<#{self.class} #{path}>"
  40. end
  41. 1 if RSpec::Support::RubyFeatures.ripper_supported?
  42. 1 RSpec::Support.require_rspec_support 'source/node'
  43. 1 RSpec::Support.require_rspec_support 'source/token'
  44. 1 def ast
  45. 3 @ast ||= begin
  46. 3 require 'ripper'
  47. 3 sexp = Ripper.sexp(source)
  48. 3 raise SyntaxError unless sexp
  49. 3 Node.new(sexp)
  50. end
  51. end
  52. 1 def tokens
  53. 3 @tokens ||= begin
  54. 3 require 'ripper'
  55. 3 tokens = Ripper.lex(source)
  56. 3 Token.tokens_from_ripper_tokens(tokens)
  57. end
  58. end
  59. 1 def nodes_by_line_number
  60. 12 @nodes_by_line_number ||= begin
  61. 392 nodes_by_line_number = ast.select(&:location).group_by { |node| node.location.line }
  62. 3 Hash.new { |hash, key| hash[key] = [] }.merge(nodes_by_line_number)
  63. end
  64. end
  65. 1 def tokens_by_line_number
  66. 6 @tokens_by_line_number ||= begin
  67. 1084 nodes_by_line_number = tokens.group_by { |token| token.location.line }
  68. 3 Hash.new { |hash, key| hash[key] = [] }.merge(nodes_by_line_number)
  69. end
  70. end
  71. end
  72. end
  73. end
  74. end

vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/location.rb

72.73% lines covered

11 relevant lines. 8 lines covered and 3 lines missed.
    
  1. 1 module RSpec
  2. 1 module Support
  3. 1 class Source
  4. # @private
  5. # Represents a source location of node or token.
  6. 1 Location = Struct.new(:line, :column) do
  7. 1 include Comparable
  8. 1 def self.location?(array)
  9. 1944 array.is_a?(Array) && array.size == 2 && array.all? { |e| e.is_a?(Integer) }
  10. end
  11. 1 def <=>(other)
  12. line_comparison = (line <=> other.line)
  13. return line_comparison unless line_comparison == 0
  14. column <=> other.column
  15. end
  16. end
  17. end
  18. end
  19. end

vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/node.rb

98.0% lines covered

50 relevant lines. 49 lines covered and 1 lines missed.
    
  1. 1 RSpec::Support.require_rspec_support 'source/location'
  2. 1 module RSpec
  3. 1 module Support
  4. 1 class Source
  5. # @private
  6. # A wrapper for Ripper AST node which is generated with `Ripper.sexp`.
  7. 1 class Node
  8. 1 include Enumerable
  9. 1 attr_reader :sexp, :parent
  10. 1 def self.sexp?(array)
  11. 2178 array.is_a?(Array) && array.first.is_a?(Symbol)
  12. end
  13. 1 def initialize(ripper_sexp, parent=nil)
  14. 1182 @sexp = ripper_sexp.freeze
  15. 1182 @parent = parent
  16. end
  17. 1 def type
  18. 14 sexp[0]
  19. end
  20. 1 def args
  21. 2408 @args ||= raw_args.map do |raw_arg|
  22. 2178 if Node.sexp?(raw_arg)
  23. 1041 Node.new(raw_arg, self)
  24. 1137 elsif Location.location?(raw_arg)
  25. 389 Location.new(*raw_arg)
  26. 748 elsif raw_arg.is_a?(Array)
  27. 138 ExpressionSequenceNode.new(raw_arg, self)
  28. else
  29. 610 raw_arg
  30. end
  31. end.freeze
  32. end
  33. 1 def children
  34. 3438 @children ||= args.select { |arg| arg.is_a?(Node) }.freeze
  35. end
  36. 1 def location
  37. 3939 @location ||= args.find { |arg| arg.is_a?(Location) }
  38. end
  39. # We use a loop here (instead of recursion) to prevent SystemStackError
  40. 1 def each
  41. 9 return to_enum(__method__) unless block_given?
  42. 9 node_queue = []
  43. 9 node_queue << self
  44. 1278 while (current_node = node_queue.shift)
  45. 1260 yield current_node
  46. 1260 node_queue.concat(current_node.children)
  47. end
  48. end
  49. 1 def each_ancestor
  50. 68 return to_enum(__method__) unless block_given?
  51. 34 current_node = self
  52. 780 while (current_node = current_node.parent)
  53. 712 yield current_node
  54. end
  55. end
  56. 1 def inspect
  57. "#<#{self.class} #{type}>"
  58. end
  59. 1 private
  60. 1 def raw_args
  61. 1044 sexp[1..-1] || []
  62. end
  63. end
  64. # @private
  65. # Basically `Ripper.sexp` generates arrays whose first element is a symbol (type of sexp),
  66. # but it exceptionally generates typeless arrays for expression sequence:
  67. #
  68. # Ripper.sexp('foo; bar')
  69. # => [
  70. # :program,
  71. # [ # Typeless array
  72. # [:vcall, [:@ident, "foo", [1, 0]]],
  73. # [:vcall, [:@ident, "bar", [1, 5]]]
  74. # ]
  75. # ]
  76. #
  77. # We wrap typeless arrays in this pseudo type node
  78. # so that it can be handled in the same way as other type node.
  79. 1 class ExpressionSequenceNode < Node
  80. 1 def type
  81. 2 :_expression_sequence
  82. end
  83. 1 private
  84. 1 def raw_args
  85. 138 sexp
  86. end
  87. end
  88. end
  89. end
  90. end

vender/bundle/ruby/2.6.0/gems/rspec-support-3.10.2/lib/rspec/support/source/token.rb

92.5% lines covered

40 relevant lines. 37 lines covered and 3 lines missed.
    
  1. 1 RSpec::Support.require_rspec_support 'source/location'
  2. 1 module RSpec
  3. 1 module Support
  4. 1 class Source
  5. # @private
  6. # A wrapper for Ripper token which is generated with `Ripper.lex`.
  7. 1 class Token
  8. CLOSING_TYPES_BY_OPENING_TYPE = {
  9. 1 :on_lbracket => :on_rbracket,
  10. :on_lparen => :on_rparen,
  11. :on_lbrace => :on_rbrace,
  12. :on_heredoc_beg => :on_heredoc_end
  13. }.freeze
  14. CLOSING_KEYWORDS_BY_OPENING_KEYWORD = {
  15. 1 'def' => 'end',
  16. 'do' => 'end',
  17. }.freeze
  18. 1 attr_reader :token
  19. 1 def self.tokens_from_ripper_tokens(ripper_tokens)
  20. 1084 ripper_tokens.map { |ripper_token| new(ripper_token) }.freeze
  21. end
  22. 1 def initialize(ripper_token)
  23. 1081 @token = ripper_token.freeze
  24. end
  25. 1 def location
  26. 1081 @location ||= Location.new(*token[0])
  27. end
  28. 1 def type
  29. 160 token[1]
  30. end
  31. 1 def string
  32. 4 token[2]
  33. end
  34. 1 def ==(other)
  35. token == other.token
  36. end
  37. 1 alias_method :eql?, :==
  38. 1 def inspect
  39. "#<#{self.class} #{type} #{string.inspect}>"
  40. end
  41. 1 def keyword?
  42. 72 type == :on_kw
  43. end
  44. 1 def opening?
  45. 64 opening_delimiter? || opening_keyword?
  46. end
  47. 1 def closed_by?(other)
  48. 12 closed_by_delimiter?(other) || closed_by_keyword?(other)
  49. end
  50. 1 private
  51. 1 def opening_delimiter?
  52. 64 CLOSING_TYPES_BY_OPENING_TYPE.key?(type)
  53. end
  54. 1 def opening_keyword?
  55. 62 return false unless keyword?
  56. 4 CLOSING_KEYWORDS_BY_OPENING_KEYWORD.key?(string)
  57. end
  58. 1 def closed_by_delimiter?(other)
  59. 12 other.type == CLOSING_TYPES_BY_OPENING_TYPE[type]
  60. end
  61. 1 def closed_by_keyword?(other)
  62. 10 return false unless other.keyword?
  63. other.string == CLOSING_KEYWORDS_BY_OPENING_KEYWORD[string]
  64. end
  65. end
  66. end
  67. end
  68. end